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,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ATSExceptionWithoutJustificationRule = void 0;
4
+ const index_js_1 = require("../../types/index.js");
5
+ const base_js_1 = require("../base.js");
6
+ const ATS_KEY = 'NSAppTransportSecurity';
7
+ const ALLOWS_ARBITRARY_LOADS_KEY = 'NSAllowsArbitraryLoads';
8
+ const ALLOWS_ARBITRARY_LOADS_WEBVIEW_KEY = 'NSAllowsArbitraryLoadsInWebContent';
9
+ const EXCEPTION_DOMAINS_KEY = 'NSExceptionDomains';
10
+ exports.ATSExceptionWithoutJustificationRule = {
11
+ id: 'config-001-ats-exception-without-justification',
12
+ name: 'ATS Exception Without Justification',
13
+ description: 'Checks for insecure App Transport Security configuration',
14
+ category: index_js_1.RuleCategory.Config,
15
+ severity: index_js_1.Severity.High,
16
+ confidence: index_js_1.Confidence.High,
17
+ guidelineReference: '2.1',
18
+ async evaluate(context) {
19
+ const atsConfig = context.infoPlist[ATS_KEY];
20
+ if (!atsConfig) {
21
+ // No ATS configuration, defaults are secure
22
+ return [];
23
+ }
24
+ const findings = [];
25
+ const allowsArbitraryLoads = atsConfig[ALLOWS_ARBITRARY_LOADS_KEY];
26
+ const allowsArbitraryLoadsWebView = atsConfig[ALLOWS_ARBITRARY_LOADS_WEBVIEW_KEY];
27
+ const exceptionDomains = atsConfig[EXCEPTION_DOMAINS_KEY];
28
+ // Case 1: NSAllowsArbitraryLoads = true without exception domains
29
+ if (allowsArbitraryLoads === true) {
30
+ const hasExceptionDomains = exceptionDomains && Object.keys(exceptionDomains).length > 0;
31
+ if (!hasExceptionDomains) {
32
+ findings.push((0, base_js_1.makeFinding)(this, {
33
+ title: 'Insecure ATS Configuration - Arbitrary Loads Enabled',
34
+ description: `Your app has NSAllowsArbitraryLoads set to true without specifying NSExceptionDomains. ` +
35
+ `This disables App Transport Security for ALL network connections, which is a significant ` +
36
+ `security risk. Apple will require justification during App Store review and may reject ` +
37
+ `your app if there's no valid reason.`,
38
+ location: 'Info.plist (NSAppTransportSecurity)',
39
+ fixGuidance: `Instead of disabling ATS entirely, configure specific exceptions for domains that ` +
40
+ `require HTTP:
41
+
42
+ OPTION 1: Remove NSAllowsArbitraryLoads entirely (recommended)
43
+ Apple strongly recommends using HTTPS for all connections.
44
+
45
+ OPTION 2: Use targeted exceptions for specific domains
46
+ <key>NSAppTransportSecurity</key>
47
+ <dict>
48
+ <key>NSExceptionDomains</key>
49
+ <dict>
50
+ <key>legacy-api.example.com</key>
51
+ <dict>
52
+ <key>NSExceptionAllowsInsecureHTTPLoads</key>
53
+ <true/>
54
+ <key>NSExceptionMinimumTLSVersion</key>
55
+ <string>TLSv1.2</string>
56
+ </dict>
57
+ </dict>
58
+ </dict>
59
+
60
+ If you must use NSAllowsArbitraryLoads, you'll need to provide App Store Connect with a justification. ` +
61
+ `Valid reasons include: connecting to servers you don't control, media streaming requirements, ` +
62
+ `or supporting legacy enterprise systems.`,
63
+ documentationURL: 'https://developer.apple.com/documentation/security/preventing_insecure_network_connections',
64
+ }));
65
+ }
66
+ else {
67
+ // Has exception domains, but also has global disable - unusual config
68
+ findings.push((0, base_js_1.makeCustomFinding)(this, index_js_1.Severity.Medium, index_js_1.Confidence.Medium, {
69
+ title: 'Redundant ATS Configuration',
70
+ description: `Your app has both NSAllowsArbitraryLoads = true AND specific NSExceptionDomains. ` +
71
+ `This is an unusual configuration. If you're using exception domains, you likely don't ` +
72
+ `need NSAllowsArbitraryLoads at all.`,
73
+ location: 'Info.plist (NSAppTransportSecurity)',
74
+ fixGuidance: `Consider removing NSAllowsArbitraryLoads and keeping only NSExceptionDomains for ` +
75
+ `the specific domains that need HTTP access. This provides better security by limiting ` +
76
+ `insecure connections to only the domains you've explicitly allowed.
77
+
78
+ Current configuration appears redundant:
79
+ - NSAllowsArbitraryLoads = true (allows everything)
80
+ - NSExceptionDomains configured (allows specific domains)
81
+
82
+ The exception domains are ignored when NSAllowsArbitraryLoads is true.`,
83
+ documentationURL: 'https://developer.apple.com/documentation/security/preventing_insecure_network_connections',
84
+ }));
85
+ }
86
+ }
87
+ // Case 2: NSAllowsArbitraryLoadsInWebContent = true
88
+ if (allowsArbitraryLoadsWebView === true) {
89
+ findings.push((0, base_js_1.makeCustomFinding)(this, index_js_1.Severity.Medium, index_js_1.Confidence.High, {
90
+ title: 'ATS Disabled for Web Content',
91
+ description: `NSAllowsArbitraryLoadsInWebContent is set to true, allowing web views to load ` +
92
+ `insecure HTTP content. While this is less severe than NSAllowsArbitraryLoads, it still ` +
93
+ `represents a security consideration.`,
94
+ location: 'Info.plist (NSAppTransportSecurity)',
95
+ fixGuidance: `If your app needs to display arbitrary web content (like a browser), this setting ` +
96
+ `may be justified. However, if your web views only load content from known sources, consider:
97
+
98
+ 1. Using NSExceptionDomains for specific domains instead
99
+ 2. Ensuring your web content servers support HTTPS
100
+
101
+ <key>NSAppTransportSecurity</key>
102
+ <dict>
103
+ <key>NSExceptionDomains</key>
104
+ <dict>
105
+ <key>trusted-content.example.com</key>
106
+ <dict>
107
+ <key>NSExceptionAllowsInsecureHTTPLoads</key>
108
+ <true/>
109
+ </dict>
110
+ </dict>
111
+ </dict>`,
112
+ documentationURL: 'https://developer.apple.com/documentation/security/preventing_insecure_network_connections',
113
+ }));
114
+ }
115
+ // Case 3: Check for overly permissive exception domains
116
+ if (exceptionDomains && typeof exceptionDomains === 'object') {
117
+ for (const [domain, config] of Object.entries(exceptionDomains)) {
118
+ const domainConfig = config;
119
+ // Check for wildcard or overly broad domains
120
+ if (domain.startsWith('*') || domain === 'localhost') {
121
+ continue; // Wildcard and localhost are sometimes valid
122
+ }
123
+ // Check if exception requires minimum TLS version
124
+ const allowsInsecure = domainConfig['NSExceptionAllowsInsecureHTTPLoads'];
125
+ // NSExceptionRequiresForwardSecrecy available in domainConfig if needed
126
+ const minTLS = domainConfig['NSExceptionMinimumTLSVersion'];
127
+ if (allowsInsecure === true && !minTLS) {
128
+ findings.push((0, base_js_1.makeCustomFinding)(this, index_js_1.Severity.Low, index_js_1.Confidence.Medium, {
129
+ title: `Insecure HTTP Allowed for ${domain}`,
130
+ description: `Domain "${domain}" allows insecure HTTP connections without specifying ` +
131
+ `a minimum TLS version. Consider if this domain can support HTTPS.`,
132
+ location: 'Info.plist (NSExceptionDomains)',
133
+ fixGuidance: `If the server supports TLS, add NSExceptionMinimumTLSVersion:
134
+
135
+ <key>${domain}</key>
136
+ <dict>
137
+ <key>NSExceptionAllowsInsecureHTTPLoads</key>
138
+ <true/>
139
+ <key>NSExceptionMinimumTLSVersion</key>
140
+ <string>TLSv1.2</string>
141
+ </dict>
142
+
143
+ Better yet, work with the server operator to enable HTTPS and remove this exception entirely.`,
144
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains',
145
+ }));
146
+ }
147
+ }
148
+ }
149
+ return findings;
150
+ },
151
+ };
152
+ //# sourceMappingURL=ats-exception-without-justification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ats-exception-without-justification.js","sourceRoot":"","sources":["../../../src/rules/config/ats-exception-without-justification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Config rules exports
3
+ */
4
+ export { ATSExceptionWithoutJustificationRule } from './ats-exception-without-justification.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/config/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,oCAAoC,EAAE,MAAM,0CAA0C,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ATSExceptionWithoutJustificationRule = void 0;
4
+ /**
5
+ * Config rules exports
6
+ */
7
+ var ats_exception_without_justification_js_1 = require("./ats-exception-without-justification.js");
8
+ Object.defineProperty(exports, "ATSExceptionWithoutJustificationRule", { enumerable: true, get: function () { return ats_exception_without_justification_js_1.ATSExceptionWithoutJustificationRule; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/config/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,mGAAgG;AAAvF,8JAAA,oCAAoC,OAAA"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Rules module - exports all rules and registry
3
+ */
4
+ import type { Rule } from '../types/index.js';
5
+ export * from './privacy/index.js';
6
+ export * from './auth/index.js';
7
+ export * from './metadata/index.js';
8
+ export * from './config/index.js';
9
+ export * from './base.js';
10
+ /**
11
+ * All available rules
12
+ */
13
+ export declare const allRules: Rule[];
14
+ /**
15
+ * Rule registry - maps rule IDs to rule instances
16
+ */
17
+ export declare const ruleRegistry: Map<string, Rule>;
18
+ /**
19
+ * Get a rule by ID
20
+ */
21
+ export declare function getRule(id: string): Rule | undefined;
22
+ /**
23
+ * Result of rule lookup with validation info
24
+ */
25
+ export interface RuleLookupResult {
26
+ rules: Rule[];
27
+ unknownIds: string[];
28
+ }
29
+ /**
30
+ * Get rules by IDs with validation (returns all if no IDs specified)
31
+ * Returns both found rules and list of unknown IDs for validation
32
+ */
33
+ export declare function getRulesWithValidation(ids?: string[]): RuleLookupResult;
34
+ /**
35
+ * Get rules by IDs (returns all if no IDs specified)
36
+ * @deprecated Use getRulesWithValidation for proper error handling
37
+ */
38
+ export declare function getRules(ids?: string[]): Rule[];
39
+ /**
40
+ * Get rules excluding specified IDs
41
+ */
42
+ export declare function getRulesExcluding(excludeIds: string[]): Rule[];
43
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAG9C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,WAAW,CAAC;AAc1B;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,IAAI,EAW1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAE1C,CAAC;AAEF;;GAEG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAEpD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAkBvE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAE/C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAG9D"}
@@ -0,0 +1,103 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ruleRegistry = exports.allRules = void 0;
18
+ exports.getRule = getRule;
19
+ exports.getRulesWithValidation = getRulesWithValidation;
20
+ exports.getRules = getRules;
21
+ exports.getRulesExcluding = getRulesExcluding;
22
+ // Privacy rules
23
+ __exportStar(require("./privacy/index.js"), exports);
24
+ // Auth rules
25
+ __exportStar(require("./auth/index.js"), exports);
26
+ // Metadata rules
27
+ __exportStar(require("./metadata/index.js"), exports);
28
+ // Config rules
29
+ __exportStar(require("./config/index.js"), exports);
30
+ // Base utilities
31
+ __exportStar(require("./base.js"), exports);
32
+ // Import all rules for registry
33
+ const missing_camera_purpose_js_1 = require("./privacy/missing-camera-purpose.js");
34
+ const missing_location_purpose_js_1 = require("./privacy/missing-location-purpose.js");
35
+ const location_always_unjustified_js_1 = require("./privacy/location-always-unjustified.js");
36
+ const att_tracking_mismatch_js_1 = require("./privacy/att-tracking-mismatch.js");
37
+ const missing_photo_library_purpose_js_1 = require("./privacy/missing-photo-library-purpose.js");
38
+ const missing_microphone_purpose_js_1 = require("./privacy/missing-microphone-purpose.js");
39
+ const missing_contacts_purpose_js_1 = require("./privacy/missing-contacts-purpose.js");
40
+ const third_party_login_no_siwa_js_1 = require("./auth/third-party-login-no-siwa.js");
41
+ const missing_privacy_manifest_js_1 = require("./metadata/missing-privacy-manifest.js");
42
+ const ats_exception_without_justification_js_1 = require("./config/ats-exception-without-justification.js");
43
+ /**
44
+ * All available rules
45
+ */
46
+ exports.allRules = [
47
+ missing_camera_purpose_js_1.MissingCameraPurposeRule,
48
+ missing_location_purpose_js_1.MissingLocationPurposeRule,
49
+ location_always_unjustified_js_1.LocationAlwaysUnjustifiedRule,
50
+ att_tracking_mismatch_js_1.ATTTrackingMismatchRule,
51
+ missing_photo_library_purpose_js_1.MissingPhotoLibraryPurposeRule,
52
+ missing_microphone_purpose_js_1.MissingMicrophonePurposeRule,
53
+ missing_contacts_purpose_js_1.MissingContactsPurposeRule,
54
+ third_party_login_no_siwa_js_1.ThirdPartyLoginNoSIWARule,
55
+ missing_privacy_manifest_js_1.MissingPrivacyManifestRule,
56
+ ats_exception_without_justification_js_1.ATSExceptionWithoutJustificationRule,
57
+ ];
58
+ /**
59
+ * Rule registry - maps rule IDs to rule instances
60
+ */
61
+ exports.ruleRegistry = new Map(exports.allRules.map(rule => [rule.id, rule]));
62
+ /**
63
+ * Get a rule by ID
64
+ */
65
+ function getRule(id) {
66
+ return exports.ruleRegistry.get(id);
67
+ }
68
+ /**
69
+ * Get rules by IDs with validation (returns all if no IDs specified)
70
+ * Returns both found rules and list of unknown IDs for validation
71
+ */
72
+ function getRulesWithValidation(ids) {
73
+ if (!ids || ids.length === 0) {
74
+ return { rules: exports.allRules, unknownIds: [] };
75
+ }
76
+ const rules = [];
77
+ const unknownIds = [];
78
+ for (const id of ids) {
79
+ const rule = exports.ruleRegistry.get(id);
80
+ if (rule) {
81
+ rules.push(rule);
82
+ }
83
+ else {
84
+ unknownIds.push(id);
85
+ }
86
+ }
87
+ return { rules, unknownIds };
88
+ }
89
+ /**
90
+ * Get rules by IDs (returns all if no IDs specified)
91
+ * @deprecated Use getRulesWithValidation for proper error handling
92
+ */
93
+ function getRules(ids) {
94
+ return getRulesWithValidation(ids).rules;
95
+ }
96
+ /**
97
+ * Get rules excluding specified IDs
98
+ */
99
+ function getRulesExcluding(excludeIds) {
100
+ const excludeSet = new Set(excludeIds);
101
+ return exports.allRules.filter(rule => !excludeSet.has(rule.id));
102
+ }
103
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AA0DA,0BAEC;AAcD,wDAkBC;AAMD,4BAEC;AAKD,8CAGC;AAvGD,gBAAgB;AAChB,qDAAmC;AAEnC,aAAa;AACb,kDAAgC;AAEhC,iBAAiB;AACjB,sDAAoC;AAEpC,eAAe;AACf,oDAAkC;AAElC,iBAAiB;AACjB,4CAA0B;AAE1B,gCAAgC;AAChC,mFAA+E;AAC/E,uFAAmF;AACnF,6FAAyF;AACzF,iFAA6E;AAC7E,iGAA4F;AAC5F,2FAAuF;AACvF,uFAAmF;AACnF,sFAAgF;AAChF,wFAAoF;AACpF,4GAAuG;AAEvG;;GAEG;AACU,QAAA,QAAQ,GAAW;IAC9B,oDAAwB;IACxB,wDAA0B;IAC1B,8DAA6B;IAC7B,kDAAuB;IACvB,iEAA8B;IAC9B,4DAA4B;IAC5B,wDAA0B;IAC1B,wDAAyB;IACzB,wDAA0B;IAC1B,6EAAoC;CACrC,CAAC;AAEF;;GAEG;AACU,QAAA,YAAY,GAAsB,IAAI,GAAG,CACpD,gBAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CACtC,CAAC;AAEF;;GAEG;AACH,SAAgB,OAAO,CAAC,EAAU;IAChC,OAAO,oBAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC;AAUD;;;GAGG;AACH,SAAgB,sBAAsB,CAAC,GAAc;IACnD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,KAAK,EAAE,gBAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,oBAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,SAAgB,QAAQ,CAAC,GAAc;IACrC,OAAO,sBAAsB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,UAAoB;IACpD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IACvC,OAAO,gBAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Metadata rules exports
3
+ */
4
+ export { MissingPrivacyManifestRule } from './missing-privacy-manifest.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/metadata/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MissingPrivacyManifestRule = void 0;
4
+ /**
5
+ * Metadata rules exports
6
+ */
7
+ var missing_privacy_manifest_js_1 = require("./missing-privacy-manifest.js");
8
+ Object.defineProperty(exports, "MissingPrivacyManifestRule", { enumerable: true, get: function () { return missing_privacy_manifest_js_1.MissingPrivacyManifestRule; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/metadata/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,6EAA2E;AAAlE,yIAAA,0BAA0B,OAAA"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Rule: Missing Privacy Manifest
3
+ *
4
+ * iOS 17+ requires PrivacyInfo.xcprivacy for apps that use certain APIs
5
+ * known as "Required Reason APIs". This rule checks for common indicators
6
+ * that an app may need a privacy manifest.
7
+ *
8
+ * App Store Review Guideline: 5.1.1
9
+ */
10
+ import type { Rule } from '../../types/index.js';
11
+ export declare const MissingPrivacyManifestRule: Rule;
12
+ //# sourceMappingURL=missing-privacy-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-privacy-manifest.d.ts","sourceRoot":"","sources":["../../../src/rules/metadata/missing-privacy-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,sBAAsB,CAAC;AA4DvE,eAAO,MAAM,0BAA0B,EAAE,IAuGxC,CAAC"}
@@ -0,0 +1,186 @@
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.MissingPrivacyManifestRule = void 0;
37
+ const index_js_1 = require("../../types/index.js");
38
+ // Note: using Privacy category as privacy manifest relates to data privacy compliance
39
+ const base_js_1 = require("../base.js");
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ /**
43
+ * SDK dependencies commonly known to require privacy manifests
44
+ */
45
+ const SDK_REQUIRING_PRIVACY_MANIFEST = [
46
+ { pattern: 'Firebase', name: 'Firebase', note: 'Firebase SDKs require privacy manifests as of May 2024' },
47
+ { pattern: 'Facebook', name: 'Facebook SDK', note: 'Facebook SDK requires privacy manifests' },
48
+ { pattern: 'FBSDK', name: 'Facebook SDK', note: 'Facebook SDK requires privacy manifests' },
49
+ { pattern: 'GoogleMobileAds', name: 'Google Mobile Ads', note: 'Google Ad SDK requires privacy manifests' },
50
+ { pattern: 'Google-Mobile-Ads', name: 'Google Mobile Ads', note: 'Google Ad SDK requires privacy manifests' },
51
+ { pattern: 'Crashlytics', name: 'Crashlytics', note: 'Crashlytics requires privacy manifests' },
52
+ { pattern: 'Amplitude', name: 'Amplitude', note: 'Amplitude SDK requires privacy manifests' },
53
+ { pattern: 'Mixpanel', name: 'Mixpanel', note: 'Mixpanel SDK requires privacy manifests' },
54
+ { pattern: 'Adjust', name: 'Adjust', note: 'Adjust SDK requires privacy manifests' },
55
+ { pattern: 'AppsFlyer', name: 'AppsFlyer', note: 'AppsFlyer SDK requires privacy manifests' },
56
+ ];
57
+ /**
58
+ * Check if a privacy manifest exists in the project
59
+ */
60
+ function findPrivacyManifest(projectPath) {
61
+ const searchPaths = [
62
+ path.join(projectPath, 'PrivacyInfo.xcprivacy'),
63
+ path.join(projectPath, 'Resources', 'PrivacyInfo.xcprivacy'),
64
+ ];
65
+ // Also search in subdirectories (common app structure)
66
+ try {
67
+ const entries = fs.readdirSync(projectPath);
68
+ for (const entry of entries) {
69
+ const fullPath = path.join(projectPath, entry);
70
+ try {
71
+ const stat = fs.statSync(fullPath);
72
+ if (stat.isDirectory()) {
73
+ searchPaths.push(path.join(fullPath, 'PrivacyInfo.xcprivacy'));
74
+ searchPaths.push(path.join(fullPath, 'Resources', 'PrivacyInfo.xcprivacy'));
75
+ }
76
+ }
77
+ catch {
78
+ // Ignore stat errors
79
+ }
80
+ }
81
+ }
82
+ catch {
83
+ // Ignore readdir errors
84
+ }
85
+ for (const searchPath of searchPaths) {
86
+ if (fs.existsSync(searchPath)) {
87
+ return searchPath;
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ exports.MissingPrivacyManifestRule = {
93
+ id: 'metadata-001-missing-privacy-manifest',
94
+ name: 'Missing Privacy Manifest',
95
+ description: 'Checks for presence of PrivacyInfo.xcprivacy when using Required Reason APIs or common SDKs',
96
+ category: index_js_1.RuleCategory.Metadata,
97
+ severity: index_js_1.Severity.High,
98
+ confidence: index_js_1.Confidence.Medium,
99
+ guidelineReference: '5.1.1',
100
+ async evaluate(context) {
101
+ // Check if privacy manifest exists
102
+ const privacyManifestPath = findPrivacyManifest(context.projectPath);
103
+ if (privacyManifestPath) {
104
+ // Privacy manifest exists, no issue
105
+ return [];
106
+ }
107
+ const findings = [];
108
+ // Check for SDKs that commonly require privacy manifests
109
+ const detectedSDKs = [];
110
+ for (const sdk of SDK_REQUIRING_PRIVACY_MANIFEST) {
111
+ const hasSDK = context.dependencies.some(dep => dep.name.toLowerCase().includes(sdk.pattern.toLowerCase()));
112
+ if (hasSDK && !detectedSDKs.includes(sdk.name)) {
113
+ detectedSDKs.push(sdk.name);
114
+ }
115
+ }
116
+ if (detectedSDKs.length > 0) {
117
+ findings.push((0, base_js_1.makeFinding)(this, {
118
+ title: 'Missing Privacy Manifest for Third-Party SDKs',
119
+ description: `Your app uses SDKs that require privacy manifests: ${detectedSDKs.join(', ')}. ` +
120
+ `Starting Spring 2024, apps submitted to the App Store must include a privacy manifest ` +
121
+ `(PrivacyInfo.xcprivacy) that declares the data collection and Required Reason API usage ` +
122
+ `from these SDKs.`,
123
+ location: context.projectPath,
124
+ fixGuidance: `Create a PrivacyInfo.xcprivacy file in your project root and declare the Required Reason APIs ` +
125
+ `used by your app and its dependencies.
126
+
127
+ 1. In Xcode, File > New > File > iOS > Resource > App Privacy
128
+ 2. Add entries for each Required Reason API category your app uses
129
+ 3. Ensure third-party SDK privacy manifests are bundled with your app
130
+
131
+ Most SDK vendors now provide privacy manifests with their SDKs. Update to the latest versions.
132
+
133
+ Example PrivacyInfo.xcprivacy structure:
134
+ <?xml version="1.0" encoding="UTF-8"?>
135
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
136
+ <plist version="1.0">
137
+ <dict>
138
+ <key>NSPrivacyTracking</key>
139
+ <false/>
140
+ <key>NSPrivacyCollectedDataTypes</key>
141
+ <array/>
142
+ <key>NSPrivacyAccessedAPITypes</key>
143
+ <array>
144
+ <dict>
145
+ <key>NSPrivacyAccessedAPIType</key>
146
+ <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
147
+ <key>NSPrivacyAccessedAPITypeReasons</key>
148
+ <array>
149
+ <string>CA92.1</string>
150
+ </array>
151
+ </dict>
152
+ </array>
153
+ </dict>
154
+ </plist>`,
155
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/privacy_manifest_files',
156
+ }));
157
+ }
158
+ // If no SDK indicators but this is a newer project, provide informational warning
159
+ if (findings.length === 0) {
160
+ // Check for frameworks that might use Required Reason APIs
161
+ const commonFrameworks = ['Foundation', 'UIKit'];
162
+ const hasCommonFrameworks = commonFrameworks.some(f => context.hasFramework(f));
163
+ if (hasCommonFrameworks && context.dependencies.length > 0) {
164
+ findings.push((0, base_js_1.makeCustomFinding)(this, index_js_1.Severity.Info, index_js_1.Confidence.Low, {
165
+ title: 'Consider Adding Privacy Manifest',
166
+ description: `Your app uses third-party dependencies which may use Required Reason APIs. ` +
167
+ `Consider adding a PrivacyInfo.xcprivacy to avoid potential App Store submission issues.`,
168
+ location: context.projectPath,
169
+ fixGuidance: `Review Apple's Required Reason API documentation and check if your app or its ` +
170
+ `dependencies use any of these APIs:
171
+
172
+ - File timestamp APIs (NSFileCreationDate, NSFileModificationDate)
173
+ - System boot time APIs (systemUptime)
174
+ - Disk space APIs (volumeAvailableCapacity)
175
+ - User defaults (UserDefaults.standard)
176
+ - Active keyboard APIs (activeInputModes)
177
+
178
+ If any are used, create a PrivacyInfo.xcprivacy and declare the appropriate reasons.`,
179
+ documentationURL: 'https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api',
180
+ }));
181
+ }
182
+ }
183
+ return findings;
184
+ },
185
+ };
186
+ //# sourceMappingURL=missing-privacy-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-privacy-manifest.js","sourceRoot":"","sources":["../../../src/rules/metadata/missing-privacy-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Rule: Tracking SDK Without App Tracking Transparency
3
+ *
4
+ * Detects when an app includes tracking/attribution SDKs but is missing
5
+ * NSUserTrackingUsageDescription in Info.plist and/or the AppTrackingTransparency
6
+ * framework.
7
+ *
8
+ * App Store Review Guideline: 5.1.2
9
+ */
10
+ import type { Rule } from '../../types/index.js';
11
+ export declare const ATTTrackingMismatchRule: Rule;
12
+ //# sourceMappingURL=att-tracking-mismatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"att-tracking-mismatch.d.ts","sourceRoot":"","sources":["../../../src/rules/privacy/att-tracking-mismatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,sBAAsB,CAAC;AASvE,eAAO,MAAM,uBAAuB,EAAE,IA6GrC,CAAC"}