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,82 @@
1
+ /**
2
+ * A project reference extracted from workspace data
3
+ */
4
+ export interface WorkspaceProjectRef {
5
+ /** The location string as it appears in the XML (e.g., "group:MyApp/MyApp.xcodeproj") */
6
+ rawLocation: string;
7
+ /** The location type (group, absolute, container, self) */
8
+ locationType: 'group' | 'absolute' | 'container' | 'self' | 'unknown';
9
+ /** The relative or absolute path to the project */
10
+ projectPath: string;
11
+ /** Whether this is a Pods project (dependency, not the main app) */
12
+ isPods: boolean;
13
+ /** Whether this is a test/example project (by path heuristics) */
14
+ isTestOrExample: boolean;
15
+ /** Product type priority score (higher = better candidate for main app) */
16
+ productTypePriority?: number;
17
+ /** Main target product type (e.g., "com.apple.product-type.application") */
18
+ productType?: string;
19
+ /** Whether the main target is an application (by productType) */
20
+ isApplication?: boolean;
21
+ /** Whether the main target is a test target (by productType) */
22
+ isTestTarget?: boolean;
23
+ }
24
+ /**
25
+ * Parsed workspace data
26
+ */
27
+ export interface ParsedWorkspace {
28
+ /** Version from the Workspace element */
29
+ version: string;
30
+ /** All project references found */
31
+ projectRefs: WorkspaceProjectRef[];
32
+ /** Main project refs (excluding Pods, tests, examples) */
33
+ mainProjectRefs: WorkspaceProjectRef[];
34
+ }
35
+ /**
36
+ * Parses the contents.xcworkspacedata file
37
+ *
38
+ * The file is XML with structure:
39
+ * ```xml
40
+ * <?xml version="1.0" encoding="UTF-8"?>
41
+ * <Workspace version="1.0">
42
+ * <FileRef location="group:MyApp/MyApp.xcodeproj"/>
43
+ * <FileRef location="group:Pods/Pods.xcodeproj"/>
44
+ * </Workspace>
45
+ * ```
46
+ *
47
+ * Location types:
48
+ * - group: relative to workspace directory
49
+ * - absolute: absolute filesystem path
50
+ * - container: relative to containing project (rare)
51
+ * - self: the workspace itself (rare)
52
+ */
53
+ export declare function parseWorkspaceData(workspacePath: string): ParsedWorkspace;
54
+ /**
55
+ * Parses workspace data from a string (for testing)
56
+ */
57
+ export declare function parseWorkspaceDataString(content: string): ParsedWorkspace;
58
+ /**
59
+ * Resolves a workspace project reference to an absolute path
60
+ *
61
+ * @param ref The project reference
62
+ * @param workspaceDir Directory containing the .xcworkspace
63
+ * @returns Absolute path to the .xcodeproj, or undefined if not resolvable
64
+ */
65
+ export declare function resolveProjectRef(ref: WorkspaceProjectRef, workspaceDir: string): string | undefined;
66
+ /**
67
+ * Given a workspace path, returns the main project paths
68
+ *
69
+ * This is the primary API for discovering projects in a workspace.
70
+ * It parses the workspace data and resolves the main project references.
71
+ *
72
+ * P1/P2 Fix: Now uses product type for scoring, not name heuristics.
73
+ * Projects are sorted by:
74
+ * 1. Product type priority (applications first)
75
+ * 2. Not being Pods
76
+ * 3. Not being a test target (by productType)
77
+ *
78
+ * @param workspacePath Path to .xcworkspace directory
79
+ * @returns Array of absolute paths to .xcodeproj directories
80
+ */
81
+ export declare function getWorkspaceProjects(workspacePath: string): string[];
82
+ //# sourceMappingURL=workspace-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/workspace-parser.ts"],"names":[],"mappings":"AAkBA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,YAAY,EAAE,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;IACtE,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,kEAAkE;IAClE,eAAe,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,0DAA0D;IAC1D,eAAe,EAAE,mBAAmB,EAAE,CAAC;CACxC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CAsBzE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CA8BzE;AA4DD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,mBAAmB,EACxB,YAAY,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS,CAgBpB;AAkCD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CAoEpE"}
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseWorkspaceData = parseWorkspaceData;
37
+ exports.parseWorkspaceDataString = parseWorkspaceDataString;
38
+ exports.resolveProjectRef = resolveProjectRef;
39
+ exports.getWorkspaceProjects = getWorkspaceProjects;
40
+ /**
41
+ * Parser for Xcode workspace files
42
+ *
43
+ * Parses contents.xcworkspacedata to extract project references.
44
+ * This allows us to find the correct project(s) instead of relying
45
+ * on directory heuristics that can pick the wrong project in monorepos.
46
+ *
47
+ * P1/P2 Fix: Now uses product type for scoring instead of name-based heuristics.
48
+ */
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const pbxproj_parser_js_1 = require("./pbxproj-parser.js");
52
+ /**
53
+ * Parses the contents.xcworkspacedata file
54
+ *
55
+ * The file is XML with structure:
56
+ * ```xml
57
+ * <?xml version="1.0" encoding="UTF-8"?>
58
+ * <Workspace version="1.0">
59
+ * <FileRef location="group:MyApp/MyApp.xcodeproj"/>
60
+ * <FileRef location="group:Pods/Pods.xcodeproj"/>
61
+ * </Workspace>
62
+ * ```
63
+ *
64
+ * Location types:
65
+ * - group: relative to workspace directory
66
+ * - absolute: absolute filesystem path
67
+ * - container: relative to containing project (rare)
68
+ * - self: the workspace itself (rare)
69
+ */
70
+ function parseWorkspaceData(workspacePath) {
71
+ // Find the contents.xcworkspacedata file
72
+ let dataPath;
73
+ if (workspacePath.endsWith('.xcworkspace')) {
74
+ dataPath = path.join(workspacePath, 'contents.xcworkspacedata');
75
+ }
76
+ else if (workspacePath.endsWith('contents.xcworkspacedata')) {
77
+ dataPath = workspacePath;
78
+ }
79
+ else {
80
+ throw new Error(`Invalid workspace path: ${workspacePath}`);
81
+ }
82
+ if (!fs.existsSync(dataPath)) {
83
+ // Return empty result if no workspace data file
84
+ return {
85
+ version: '',
86
+ projectRefs: [],
87
+ mainProjectRefs: [],
88
+ };
89
+ }
90
+ const content = fs.readFileSync(dataPath, 'utf-8');
91
+ return parseWorkspaceDataString(content);
92
+ }
93
+ /**
94
+ * Parses workspace data from a string (for testing)
95
+ */
96
+ function parseWorkspaceDataString(content) {
97
+ // Extract version from Workspace element
98
+ const versionMatch = content.match(/<Workspace\s+version="([^"]+)"/);
99
+ const version = versionMatch ? versionMatch[1] : '';
100
+ // Extract all FileRef locations
101
+ const fileRefRegex = /<FileRef\s+location="([^"]+)"\s*\/>/g;
102
+ const projectRefs = [];
103
+ let match;
104
+ while ((match = fileRefRegex.exec(content)) !== null) {
105
+ const rawLocation = match[1];
106
+ const ref = parseLocationString(rawLocation);
107
+ // Only include .xcodeproj references
108
+ if (ref.projectPath.endsWith('.xcodeproj')) {
109
+ projectRefs.push(ref);
110
+ }
111
+ }
112
+ // Filter to main projects (non-Pods, non-test, non-example)
113
+ const mainProjectRefs = projectRefs.filter(ref => !ref.isPods && !ref.isTestOrExample);
114
+ return {
115
+ version,
116
+ projectRefs,
117
+ mainProjectRefs,
118
+ };
119
+ }
120
+ /**
121
+ * Parses a location string into its components
122
+ */
123
+ function parseLocationString(rawLocation) {
124
+ // Split on first colon
125
+ const colonIndex = rawLocation.indexOf(':');
126
+ let locationType;
127
+ let projectPath;
128
+ if (colonIndex === -1) {
129
+ // No colon = treat as relative path
130
+ locationType = 'unknown';
131
+ projectPath = rawLocation;
132
+ }
133
+ else {
134
+ const typeStr = rawLocation.substring(0, colonIndex);
135
+ projectPath = rawLocation.substring(colonIndex + 1);
136
+ switch (typeStr) {
137
+ case 'group':
138
+ locationType = 'group';
139
+ break;
140
+ case 'absolute':
141
+ locationType = 'absolute';
142
+ break;
143
+ case 'container':
144
+ locationType = 'container';
145
+ break;
146
+ case 'self':
147
+ locationType = 'self';
148
+ break;
149
+ default:
150
+ locationType = 'unknown';
151
+ }
152
+ }
153
+ // Detect Pods projects
154
+ const isPods = projectPath.includes('Pods.xcodeproj') ||
155
+ projectPath.includes('/Pods/') ||
156
+ projectPath.startsWith('Pods/');
157
+ // Detect test/example projects by path segments (not substring)
158
+ // This avoids false positives like "BestApp", "ContestManager", "LatestNews"
159
+ const pathSegments = projectPath.toLowerCase().split('/');
160
+ const testKeywords = ['test', 'tests', 'example', 'examples', 'demo', 'demos', 'sample', 'samples'];
161
+ const isTestOrExample = pathSegments.some(segment => testKeywords.some(keyword => segment === keyword || segment.endsWith('tests') || segment.endsWith('test')));
162
+ return {
163
+ rawLocation,
164
+ locationType,
165
+ projectPath,
166
+ isPods,
167
+ isTestOrExample,
168
+ };
169
+ }
170
+ /**
171
+ * Resolves a workspace project reference to an absolute path
172
+ *
173
+ * @param ref The project reference
174
+ * @param workspaceDir Directory containing the .xcworkspace
175
+ * @returns Absolute path to the .xcodeproj, or undefined if not resolvable
176
+ */
177
+ function resolveProjectRef(ref, workspaceDir) {
178
+ switch (ref.locationType) {
179
+ case 'group':
180
+ // Relative to workspace parent directory
181
+ return path.resolve(workspaceDir, ref.projectPath);
182
+ case 'absolute':
183
+ // Already absolute
184
+ return ref.projectPath;
185
+ case 'container':
186
+ case 'self':
187
+ case 'unknown':
188
+ // Try as relative path
189
+ return path.resolve(workspaceDir, ref.projectPath);
190
+ }
191
+ }
192
+ /**
193
+ * Enrich a project reference with product type info by parsing its pbxproj
194
+ *
195
+ * @param ref The project reference
196
+ * @param resolvedPath The resolved absolute path to the .xcodeproj
197
+ */
198
+ function enrichProjectRefWithProductType(ref, resolvedPath) {
199
+ const pbxprojPath = path.join(resolvedPath, 'project.pbxproj');
200
+ if (!fs.existsSync(pbxprojPath)) {
201
+ return;
202
+ }
203
+ try {
204
+ const content = fs.readFileSync(pbxprojPath, 'utf-8');
205
+ const projectName = path.basename(resolvedPath).replace('.xcodeproj', '');
206
+ const productType = (0, pbxproj_parser_js_1.getMainTargetProductType)(content, projectName);
207
+ if (productType) {
208
+ ref.productType = productType;
209
+ ref.productTypePriority = (0, pbxproj_parser_js_1.getProductTypePriority)(productType);
210
+ ref.isApplication = (0, pbxproj_parser_js_1.isApplicationType)(productType);
211
+ ref.isTestTarget = (0, pbxproj_parser_js_1.isTestType)(productType);
212
+ }
213
+ }
214
+ catch {
215
+ // Ignore parsing errors - fall back to path heuristics
216
+ }
217
+ }
218
+ /**
219
+ * Given a workspace path, returns the main project paths
220
+ *
221
+ * This is the primary API for discovering projects in a workspace.
222
+ * It parses the workspace data and resolves the main project references.
223
+ *
224
+ * P1/P2 Fix: Now uses product type for scoring, not name heuristics.
225
+ * Projects are sorted by:
226
+ * 1. Product type priority (applications first)
227
+ * 2. Not being Pods
228
+ * 3. Not being a test target (by productType)
229
+ *
230
+ * @param workspacePath Path to .xcworkspace directory
231
+ * @returns Array of absolute paths to .xcodeproj directories
232
+ */
233
+ function getWorkspaceProjects(workspacePath) {
234
+ const workspaceDir = workspacePath.endsWith('.xcworkspace')
235
+ ? path.dirname(workspacePath)
236
+ : path.dirname(path.dirname(workspacePath));
237
+ const parsed = parseWorkspaceData(workspacePath);
238
+ // Resolve all project paths and enrich with product type
239
+ const enrichedRefs = [];
240
+ for (const ref of parsed.projectRefs) {
241
+ const resolved = resolveProjectRef(ref, workspaceDir);
242
+ if (resolved && fs.existsSync(resolved)) {
243
+ // Enrich with product type info
244
+ enrichProjectRefWithProductType(ref, resolved);
245
+ enrichedRefs.push({ ref, resolved });
246
+ }
247
+ }
248
+ // Sort by product type priority (P1/P2 fix: use productType, not name)
249
+ enrichedRefs.sort((a, b) => {
250
+ // First: exclude Pods (always last)
251
+ if (a.ref.isPods !== b.ref.isPods) {
252
+ return a.ref.isPods ? 1 : -1;
253
+ }
254
+ // Second: prefer applications by productType (not name heuristics)
255
+ if (a.ref.isApplication !== b.ref.isApplication) {
256
+ return a.ref.isApplication ? -1 : 1;
257
+ }
258
+ // Third: exclude test targets by productType (more reliable than name)
259
+ if (a.ref.isTestTarget !== b.ref.isTestTarget) {
260
+ return a.ref.isTestTarget ? 1 : -1;
261
+ }
262
+ // Fourth: sort by product type priority
263
+ const priorityA = a.ref.productTypePriority ?? 0;
264
+ const priorityB = b.ref.productTypePriority ?? 0;
265
+ if (priorityA !== priorityB) {
266
+ return priorityB - priorityA;
267
+ }
268
+ // Fifth: fall back to path heuristics for test/example
269
+ if (a.ref.isTestOrExample !== b.ref.isTestOrExample) {
270
+ return a.ref.isTestOrExample ? 1 : -1;
271
+ }
272
+ return 0;
273
+ });
274
+ // Filter to "main" projects for backward compatibility
275
+ // Main = application type or (not Pods and not test target)
276
+ const mainProjects = enrichedRefs.filter(({ ref }) => ref.isApplication || (!ref.isPods && !ref.isTestTarget && !ref.isTestOrExample));
277
+ // If no main projects found, return all non-test projects
278
+ const resultRefs = mainProjects.length > 0
279
+ ? mainProjects
280
+ : enrichedRefs.filter(({ ref }) => !ref.isTestTarget);
281
+ // If still nothing, return all
282
+ if (resultRefs.length === 0) {
283
+ return enrichedRefs.map(({ resolved }) => resolved);
284
+ }
285
+ return resultRefs.map(({ resolved }) => resolved);
286
+ }
287
+ //# sourceMappingURL=workspace-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-parser.js","sourceRoot":"","sources":["../../src/parsers/workspace-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Auth rules exports
3
+ */
4
+ export { ThirdPartyLoginNoSIWARule } from './third-party-login-no-siwa.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThirdPartyLoginNoSIWARule = void 0;
4
+ /**
5
+ * Auth rules exports
6
+ */
7
+ var third_party_login_no_siwa_js_1 = require("./third-party-login-no-siwa.js");
8
+ Object.defineProperty(exports, "ThirdPartyLoginNoSIWARule", { enumerable: true, get: function () { return third_party_login_no_siwa_js_1.ThirdPartyLoginNoSIWARule; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/auth/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Rule: Third-Party Login Without Sign in with Apple
3
+ *
4
+ * Detects when an app includes social login SDKs but doesn't implement
5
+ * Sign in with Apple as an equivalent option.
6
+ *
7
+ * App Store Review Guideline: 4.8
8
+ */
9
+ import type { Rule } from '../../types/index.js';
10
+ export declare const ThirdPartyLoginNoSIWARule: Rule;
11
+ //# sourceMappingURL=third-party-login-no-siwa.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"third-party-login-no-siwa.d.ts","sourceRoot":"","sources":["../../../src/rules/auth/third-party-login-no-siwa.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,sBAAsB,CAAC;AAcvE,eAAO,MAAM,yBAAyB,EAAE,IAkHvC,CAAC"}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThirdPartyLoginNoSIWARule = void 0;
4
+ const index_js_1 = require("../../types/index.js");
5
+ const framework_detector_js_1 = require("../../parsers/framework-detector.js");
6
+ const entitlements_parser_js_1 = require("../../parsers/entitlements-parser.js");
7
+ const base_js_1 = require("../base.js");
8
+ const AUTH_SERVICES_FRAMEWORK = 'AuthenticationServices';
9
+ // SDKs that definitively indicate social login
10
+ const DEFINITIVE_SOCIAL_SDKS = ['Google Sign-In', 'Facebook Login', 'Twitter Login', 'Login with Amazon', 'LinkedIn Login'];
11
+ // SDKs that may be used for email/password only
12
+ const AMBIGUOUS_SDKS = ['Firebase Auth', 'Auth0'];
13
+ exports.ThirdPartyLoginNoSIWARule = {
14
+ id: 'auth-001-third-party-login-no-siwa',
15
+ name: 'Third-Party Login Without Sign in with Apple',
16
+ description: 'Checks for social login SDKs without Sign in with Apple implementation',
17
+ category: index_js_1.RuleCategory.Auth,
18
+ severity: index_js_1.Severity.Critical,
19
+ confidence: index_js_1.Confidence.High,
20
+ guidelineReference: '4.8',
21
+ async evaluate(context) {
22
+ // Detect social login SDKs
23
+ const detectedSDKs = (0, framework_detector_js_1.detectSocialLoginSDKs)(context.dependencies);
24
+ if (detectedSDKs.length === 0) {
25
+ return [];
26
+ }
27
+ // Separate definitive and ambiguous SDKs
28
+ const definitiveSocialSDKs = detectedSDKs.filter(sdk => DEFINITIVE_SOCIAL_SDKS.includes(sdk));
29
+ const ambiguousSDKs = detectedSDKs.filter(sdk => AMBIGUOUS_SDKS.includes(sdk));
30
+ // Check for SIWA capability
31
+ const hasSIWAEntitlement = context.hasEntitlement(entitlements_parser_js_1.EntitlementKeys.signInWithApple);
32
+ // Check for AuthenticationServices framework
33
+ const hasAuthServices = context.hasFramework(AUTH_SERVICES_FRAMEWORK);
34
+ const findings = [];
35
+ // If definitive social SDKs are present (Google Sign-In, Facebook Login, etc.)
36
+ if (definitiveSocialSDKs.length > 0) {
37
+ // Case 1: No SIWA entitlement
38
+ if (!hasSIWAEntitlement) {
39
+ findings.push((0, base_js_1.makeFinding)(this, {
40
+ description: `Your app includes third-party social login SDKs (${definitiveSocialSDKs.join(', ')}) ` +
41
+ `but the Sign in with Apple capability is not configured. According to App Store Review ` +
42
+ `Guideline 4.8, apps that offer third-party social login must also offer Sign in with Apple ` +
43
+ `as an equivalent option.`,
44
+ location: 'Entitlements',
45
+ fixGuidance: `Add Sign in with Apple to your app:
46
+
47
+ 1. In Xcode, select your app target → Signing & Capabilities
48
+ 2. Click "+ Capability" and add "Sign in with Apple"
49
+ 3. Implement the SIWA UI alongside your existing login options:
50
+
51
+ import AuthenticationServices
52
+
53
+ // Add the Apple sign-in button to your login screen
54
+ let button = ASAuthorizationAppleIDButton(type: .signIn, style: .black)
55
+ button.addTarget(self, action: #selector(handleAppleSignIn), for: .touchUpInside)
56
+
57
+ @objc func handleAppleSignIn() {
58
+ let provider = ASAuthorizationAppleIDProvider()
59
+ let request = provider.createRequest()
60
+ request.requestedScopes = [.fullName, .email]
61
+
62
+ let controller = ASAuthorizationController(authorizationRequests: [request])
63
+ controller.delegate = self
64
+ controller.performRequests()
65
+ }
66
+
67
+ Important: Sign in with Apple must be presented as an equivalent option - same ` +
68
+ `prominence as other social login buttons.`,
69
+ documentationURL: 'https://developer.apple.com/sign-in-with-apple/',
70
+ }));
71
+ }
72
+ // Case 2: Has entitlement but no AuthenticationServices framework
73
+ else if (!hasAuthServices) {
74
+ findings.push((0, base_js_1.makeCustomFinding)(this, index_js_1.Severity.Medium, index_js_1.Confidence.Medium, {
75
+ title: 'Sign in with Apple May Not Be Implemented',
76
+ description: `Your app has the Sign in with Apple capability enabled but ` +
77
+ `AuthenticationServices framework doesn't appear to be linked. This may indicate ` +
78
+ `an incomplete SIWA implementation.`,
79
+ location: 'Project',
80
+ fixGuidance: `Ensure you're importing AuthenticationServices and implementing the sign-in flow:
81
+
82
+ import AuthenticationServices
83
+
84
+ // Present the sign-in button and handle the flow
85
+ // See Apple's documentation for complete implementation
86
+
87
+ Note: If you're using a third-party library that wraps SIWA, you can ignore ` +
88
+ `this finding.`,
89
+ documentationURL: 'https://developer.apple.com/sign-in-with-apple/',
90
+ }));
91
+ }
92
+ }
93
+ // If only ambiguous SDKs (Firebase Auth, Auth0) - lower confidence
94
+ if (definitiveSocialSDKs.length === 0 && ambiguousSDKs.length > 0 && !hasSIWAEntitlement) {
95
+ findings.push((0, base_js_1.makeCustomFinding)(this, index_js_1.Severity.Medium, index_js_1.Confidence.Medium, {
96
+ title: 'Potential Social Login Without Sign in with Apple',
97
+ description: `Your app includes authentication SDKs (${ambiguousSDKs.join(', ')}) that may ` +
98
+ `be configured for social login. If you offer Google, Facebook, or other social login ` +
99
+ `options, you must also offer Sign in with Apple.`,
100
+ location: 'Entitlements',
101
+ fixGuidance: `Review your authentication implementation:
102
+
103
+ **If you use social login (Google, Facebook, etc.):**
104
+ Add Sign in with Apple capability and implement it as an equivalent option.
105
+
106
+ **If you only use email/password authentication:**
107
+ You're exempt from Guideline 4.8. Sign in with Apple is not required for ` +
108
+ `apps that don't offer third-party social login.
109
+
110
+ **If using Firebase Auth with social providers:**
111
+ Firebase supports Sign in with Apple - add it as a provider:
112
+ https://firebase.google.com/docs/auth/ios/apple`,
113
+ documentationURL: 'https://developer.apple.com/sign-in-with-apple/',
114
+ }));
115
+ }
116
+ return findings;
117
+ },
118
+ };
119
+ //# sourceMappingURL=third-party-login-no-siwa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"third-party-login-no-siwa.js","sourceRoot":"","sources":["../../../src/rules/auth/third-party-login-no-siwa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Base utilities for rules
3
+ */
4
+ import type { Rule, Finding, Severity, Confidence } from '../types/index.js';
5
+ /**
6
+ * Helper to create a finding with common rule properties
7
+ */
8
+ export declare function makeFinding(rule: Pick<Rule, 'id' | 'name' | 'severity' | 'confidence' | 'guidelineReference'>, options: {
9
+ title?: string;
10
+ description: string;
11
+ location?: string;
12
+ fixGuidance: string;
13
+ documentationURL?: string;
14
+ }): Finding;
15
+ /**
16
+ * Creates a finding with custom severity/confidence
17
+ */
18
+ export declare function makeCustomFinding(rule: Pick<Rule, 'id' | 'guidelineReference'>, severity: Severity, confidence: Confidence, options: {
19
+ title: string;
20
+ description: string;
21
+ location?: string;
22
+ fixGuidance: string;
23
+ documentationURL?: string;
24
+ }): Finding;
25
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/rules/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE7E;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,oBAAoB,CAAC,EAClF,OAAO,EAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAYT;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,oBAAoB,CAAC,EAC7C,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE;IACP,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,OAAO,CAYT"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeFinding = makeFinding;
4
+ exports.makeCustomFinding = makeCustomFinding;
5
+ /**
6
+ * Helper to create a finding with common rule properties
7
+ */
8
+ function makeFinding(rule, options) {
9
+ return {
10
+ ruleId: rule.id,
11
+ severity: rule.severity,
12
+ confidence: rule.confidence,
13
+ title: options.title ?? rule.name,
14
+ description: options.description,
15
+ location: options.location,
16
+ guideline: rule.guidelineReference,
17
+ fixGuidance: options.fixGuidance,
18
+ documentationURL: options.documentationURL,
19
+ };
20
+ }
21
+ /**
22
+ * Creates a finding with custom severity/confidence
23
+ */
24
+ function makeCustomFinding(rule, severity, confidence, options) {
25
+ return {
26
+ ruleId: rule.id,
27
+ severity,
28
+ confidence,
29
+ title: options.title,
30
+ description: options.description,
31
+ location: options.location,
32
+ guideline: rule.guidelineReference,
33
+ fixGuidance: options.fixGuidance,
34
+ documentationURL: options.documentationURL,
35
+ };
36
+ }
37
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/rules/base.ts"],"names":[],"mappings":";;AAQA,kCAqBC;AAKD,8CAuBC;AApDD;;GAEG;AACH,SAAgB,WAAW,CACzB,IAAkF,EAClF,OAMC;IAED,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI;QACjC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,IAAI,CAAC,kBAAkB;QAClC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAC/B,IAA6C,EAC7C,QAAkB,EAClB,UAAsB,EACtB,OAMC;IAED,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,QAAQ;QACR,UAAU;QACV,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,IAAI,CAAC,kBAAkB;QAClC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Rule: ATS Exception Without Justification
3
+ *
4
+ * Detects when an app disables App Transport Security without proper
5
+ * exception configuration. NSAllowsArbitraryLoads = true is a security
6
+ * risk and commonly flagged during App Store review.
7
+ *
8
+ * App Store Review Guideline: 2.1 (App Completeness)
9
+ */
10
+ import type { Rule } from '../../types/index.js';
11
+ export declare const ATSExceptionWithoutJustificationRule: Rule;
12
+ //# sourceMappingURL=ats-exception-without-justification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ats-exception-without-justification.d.ts","sourceRoot":"","sources":["../../../src/rules/config/ats-exception-without-justification.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,sBAAsB,CAAC;AASvE,eAAO,MAAM,oCAAoC,EAAE,IAwJlD,CAAC"}