shiplint 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -21
- package/dist/cli/analytics.d.ts +62 -0
- package/dist/cli/analytics.d.ts.map +1 -0
- package/dist/cli/analytics.js +85 -0
- package/dist/cli/analytics.js.map +1 -0
- package/dist/cli/index.js +35 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/scanner.d.ts +0 -3
- package/dist/core/scanner.d.ts.map +1 -1
- package/dist/core/scanner.js +81 -0
- package/dist/core/scanner.js.map +1 -1
- package/dist/formatters/sarif.d.ts.map +1 -1
- package/dist/formatters/sarif.js +5 -1
- package/dist/formatters/sarif.js.map +1 -1
- package/dist/formatters/text.d.ts.map +1 -1
- package/dist/formatters/text.js +1 -0
- package/dist/formatters/text.js.map +1 -1
- package/dist/mcp/sampling.d.ts +45 -0
- package/dist/mcp/sampling.d.ts.map +1 -0
- package/dist/mcp/sampling.js +113 -0
- package/dist/mcp/sampling.js.map +1 -0
- package/dist/mcp/server.d.ts +16 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +315 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/parsers/framework-detector.d.ts +5 -0
- package/dist/parsers/framework-detector.d.ts.map +1 -1
- package/dist/parsers/framework-detector.js +55 -0
- package/dist/parsers/framework-detector.js.map +1 -1
- package/dist/parsers/plist-parser.d.ts.map +1 -1
- package/dist/parsers/plist-parser.js +20 -9
- package/dist/parsers/plist-parser.js.map +1 -1
- package/dist/parsers/project-parser.d.ts +1 -1
- package/dist/parsers/project-parser.d.ts.map +1 -1
- package/dist/parsers/project-parser.js +16 -2
- package/dist/parsers/project-parser.js.map +1 -1
- package/dist/rules/auth/third-party-login-no-siwa.js +3 -3
- package/dist/rules/auth/third-party-login-no-siwa.js.map +1 -1
- package/dist/rules/config/ats-exception-without-justification.d.ts.map +1 -1
- package/dist/rules/config/ats-exception-without-justification.js +5 -4
- package/dist/rules/config/ats-exception-without-justification.js.map +1 -1
- package/dist/rules/config/missing-encryption-flag.js +1 -1
- package/dist/rules/config/missing-encryption-flag.js.map +1 -1
- package/dist/rules/config/missing-launch-storyboard.js +1 -1
- package/dist/rules/config/missing-launch-storyboard.js.map +1 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +2 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/metadata/missing-supported-orientations.js +2 -2
- package/dist/rules/metadata/missing-supported-orientations.js.map +1 -1
- package/dist/rules/privacy/att-tracking-mismatch.js +4 -4
- package/dist/rules/privacy/att-tracking-mismatch.js.map +1 -1
- package/dist/rules/privacy/index.d.ts +1 -0
- package/dist/rules/privacy/index.d.ts.map +1 -1
- package/dist/rules/privacy/index.js +3 -1
- package/dist/rules/privacy/index.js.map +1 -1
- package/dist/rules/privacy/location-always-unjustified.js +3 -3
- package/dist/rules/privacy/location-always-unjustified.js.map +1 -1
- package/dist/rules/privacy/missing-bluetooth-purpose.js +3 -3
- package/dist/rules/privacy/missing-bluetooth-purpose.js.map +1 -1
- package/dist/rules/privacy/missing-camera-purpose.js +3 -3
- package/dist/rules/privacy/missing-camera-purpose.js.map +1 -1
- package/dist/rules/privacy/missing-contacts-purpose.js +3 -3
- package/dist/rules/privacy/missing-contacts-purpose.js.map +1 -1
- package/dist/rules/privacy/missing-face-id-purpose.js +3 -3
- package/dist/rules/privacy/missing-face-id-purpose.js.map +1 -1
- package/dist/rules/privacy/missing-location-purpose.js +6 -6
- package/dist/rules/privacy/missing-location-purpose.js.map +1 -1
- package/dist/rules/privacy/missing-microphone-purpose.js +6 -6
- package/dist/rules/privacy/missing-microphone-purpose.js.map +1 -1
- package/dist/rules/privacy/missing-photo-library-purpose.js +5 -5
- package/dist/rules/privacy/missing-photo-library-purpose.js.map +1 -1
- package/dist/rules/privacy/required-reason-api.d.ts +39 -0
- package/dist/rules/privacy/required-reason-api.d.ts.map +1 -0
- package/dist/rules/privacy/required-reason-api.js +310 -0
- package/dist/rules/privacy/required-reason-api.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ shiplint scan ./MyApp.xcodeproj
|
|
|
56
56
|
|
|
57
57
|
🔍 Found 3 issue(s):
|
|
58
58
|
|
|
59
|
-
1. [CRITICAL] missing-camera-purpose
|
|
59
|
+
1. [CRITICAL] privacy-001-missing-camera-purpose
|
|
60
60
|
📍 Info.plist • 📋 Guideline 5.1.1 • ⚠️ ITMS-90683
|
|
61
61
|
|
|
62
62
|
Your app references AVFoundation but Info.plist is missing
|
|
@@ -68,14 +68,14 @@ shiplint scan ./MyApp.xcodeproj
|
|
|
68
68
|
<key>NSCameraUsageDescription</key>
|
|
69
69
|
<string>This app uses the camera to scan QR codes.</string>
|
|
70
70
|
|
|
71
|
-
2. [CRITICAL] missing-privacy-manifest
|
|
71
|
+
2. [CRITICAL] metadata-001-missing-privacy-manifest
|
|
72
72
|
📍 Project • 📋 Required Reason API • ⚠️ ITMS-91053
|
|
73
73
|
|
|
74
74
|
Your project uses APIs that require a privacy manifest
|
|
75
75
|
(PrivacyInfo.xcprivacy) as of Spring 2024. Without it,
|
|
76
76
|
App Store Connect will reject your binary.
|
|
77
77
|
|
|
78
|
-
3. [WARNING] third-party-login-no-siwa
|
|
78
|
+
3. [WARNING] auth-001-third-party-login-no-siwa
|
|
79
79
|
📍 Entitlements • 📋 Guideline 4.8
|
|
80
80
|
|
|
81
81
|
Your app includes Google Sign-In but Sign in with Apple
|
|
@@ -94,56 +94,56 @@ These rules prevent **ITMS-90683** ("Missing purpose string in Info.plist"). App
|
|
|
94
94
|
|
|
95
95
|
| Rule | Info.plist Key | What It Catches |
|
|
96
96
|
|------|---------------|-----------------|
|
|
97
|
-
| `missing-camera-purpose` | `NSCameraUsageDescription` | App uses AVFoundation/camera APIs without declaring why |
|
|
98
|
-
| `missing-microphone-purpose` | `NSMicrophoneUsageDescription` | App uses audio recording APIs without declaring why |
|
|
99
|
-
| `missing-location-purpose` | `NSLocationWhenInUseUsageDescription` | App uses CoreLocation without declaring why |
|
|
100
|
-
| `missing-photo-library-purpose` | `NSPhotoLibraryUsageDescription` | App accesses Photos without declaring why |
|
|
101
|
-
| `missing-contacts-purpose` | `NSContactsUsageDescription` | App accesses Contacts without declaring why |
|
|
102
|
-
| `missing-bluetooth-purpose` | `NSBluetoothAlwaysUsageDescription` | App uses CoreBluetooth without declaring why |
|
|
103
|
-
| `missing-face-id-purpose` | `NSFaceIDUsageDescription` | App uses LocalAuthentication (Face ID) without declaring why |
|
|
104
|
-
| `location-always-unjustified` | `NSLocationAlwaysAndWhenInUseUsageDescription` | App requests "Always" location without sufficient justification — almost always rejected per [Guideline 5.1.2](https://developer.apple.com/app-store/review/guidelines/#data-use-and-sharing) |
|
|
97
|
+
| `privacy-001-missing-camera-purpose` | `NSCameraUsageDescription` | App uses AVFoundation/camera APIs without declaring why |
|
|
98
|
+
| `privacy-005-missing-microphone-purpose` | `NSMicrophoneUsageDescription` | App uses audio recording APIs without declaring why |
|
|
99
|
+
| `privacy-002-missing-location-purpose` | `NSLocationWhenInUseUsageDescription` | App uses CoreLocation without declaring why |
|
|
100
|
+
| `privacy-004-missing-photo-library-purpose` | `NSPhotoLibraryUsageDescription` | App accesses Photos without declaring why |
|
|
101
|
+
| `privacy-006-missing-contacts-purpose` | `NSContactsUsageDescription` | App accesses Contacts without declaring why |
|
|
102
|
+
| `privacy-008-missing-bluetooth-purpose` | `NSBluetoothAlwaysUsageDescription` | App uses CoreBluetooth without declaring why |
|
|
103
|
+
| `privacy-009-missing-face-id-purpose` | `NSFaceIDUsageDescription` | App uses LocalAuthentication (Face ID) without declaring why |
|
|
104
|
+
| `privacy-007-location-always-unjustified` | `NSLocationAlwaysAndWhenInUseUsageDescription` | App requests "Always" location without sufficient justification — almost always rejected per [Guideline 5.1.2](https://developer.apple.com/app-store/review/guidelines/#data-use-and-sharing) |
|
|
105
105
|
|
|
106
106
|
### App Tracking Transparency — [Guideline 5.1.2](https://developer.apple.com/app-store/review/guidelines/#data-use-and-sharing)
|
|
107
107
|
|
|
108
108
|
| Rule | What It Catches |
|
|
109
109
|
|------|-----------------|
|
|
110
|
-
| `att-tracking-mismatch` | App imports `AdSupport` or `AppTrackingTransparency` framework but `Info.plist` is missing `NSUserTrackingUsageDescription`. Required since iOS 14.5. [Apple ATT documentation](https://developer.apple.com/documentation/apptrackingtransparency) |
|
|
110
|
+
| `privacy-003-att-tracking-mismatch` | App imports `AdSupport` or `AppTrackingTransparency` framework but `Info.plist` is missing `NSUserTrackingUsageDescription`. Required since iOS 14.5. [Apple ATT documentation](https://developer.apple.com/documentation/apptrackingtransparency) |
|
|
111
111
|
|
|
112
112
|
### Sign in with Apple — [Guideline 4.8](https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple)
|
|
113
113
|
|
|
114
114
|
| Rule | What It Catches |
|
|
115
115
|
|------|-----------------|
|
|
116
|
-
| `third-party-login-no-siwa` | App uses a third-party login SDK (Google, Facebook, etc.) but Sign in with Apple is not configured. Required since [WWDC19](https://developer.apple.com/videos/play/wwdc2019/706/) for apps offering third-party sign-in. |
|
|
116
|
+
| `auth-001-third-party-login-no-siwa` | App uses a third-party login SDK (Google, Facebook, etc.) but Sign in with Apple is not configured. Required since [WWDC19](https://developer.apple.com/videos/play/wwdc2019/706/) for apps offering third-party sign-in. |
|
|
117
117
|
|
|
118
118
|
### App Transport Security — [Guideline 2.1](https://developer.apple.com/app-store/review/guidelines/#performance)
|
|
119
119
|
|
|
120
120
|
| Rule | What It Catches |
|
|
121
121
|
|------|-----------------|
|
|
122
|
-
| `ats-exception-without-justification` | App sets `NSAllowsArbitraryLoads = YES` or declares ATS exceptions without justification. Apple expects all network traffic to use HTTPS. [Apple ATS documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity) |
|
|
122
|
+
| `config-001-ats-exception-without-justification` | App sets `NSAllowsArbitraryLoads = YES` or declares ATS exceptions without justification. Apple expects all network traffic to use HTTPS. [Apple ATS documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity) |
|
|
123
123
|
|
|
124
124
|
### Privacy Manifests (iOS 17+) — [WWDC23](https://developer.apple.com/videos/play/wwdc2023/10060/)
|
|
125
125
|
|
|
126
126
|
| Rule | What It Catches |
|
|
127
127
|
|------|-----------------|
|
|
128
|
-
| `missing-privacy-manifest` | Project uses [required reason APIs](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api) (UserDefaults, file timestamps, etc.) or third-party SDKs that require a `PrivacyInfo.xcprivacy` file. Enforced by App Store Connect since Spring 2024 via **ITMS-91053**. |
|
|
128
|
+
| `metadata-001-missing-privacy-manifest` | Project uses [required reason APIs](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api) (UserDefaults, file timestamps, etc.) or third-party SDKs that require a `PrivacyInfo.xcprivacy` file. Enforced by App Store Connect since Spring 2024 via **ITMS-91053**. |
|
|
129
129
|
|
|
130
130
|
### Export Compliance — [Apple Export Compliance](https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption)
|
|
131
131
|
|
|
132
132
|
| Rule | Info.plist Key | What It Catches |
|
|
133
133
|
|------|---------------|-----------------|
|
|
134
|
-
| `missing-encryption-flag` | `ITSAppUsesNonExemptEncryption` | Missing export compliance declaration. Without this key, App Store Connect prompts for manual compliance answers on every upload — adding friction and potential delays. Set to `false` if your app only uses HTTPS or standard iOS encryption. |
|
|
134
|
+
| `config-002-missing-encryption-flag` | `ITSAppUsesNonExemptEncryption` | Missing export compliance declaration. Without this key, App Store Connect prompts for manual compliance answers on every upload — adding friction and potential delays. Set to `false` if your app only uses HTTPS or standard iOS encryption. |
|
|
135
135
|
|
|
136
136
|
### Launch Configuration — [Guideline 4.0 (Design)](https://developer.apple.com/app-store/review/guidelines/#design)
|
|
137
137
|
|
|
138
138
|
| Rule | Info.plist Key | What It Catches |
|
|
139
139
|
|------|---------------|-----------------|
|
|
140
|
-
| `missing-launch-storyboard` | `UILaunchStoryboardName` | Missing launch storyboard. Required since April 2020 for all iOS apps to support all screen sizes. Apps without this key are rejected. |
|
|
140
|
+
| `config-003-missing-launch-storyboard` | `UILaunchStoryboardName` | Missing launch storyboard. Required since April 2020 for all iOS apps to support all screen sizes. Apps without this key are rejected. |
|
|
141
141
|
|
|
142
142
|
### App Configuration — [Guideline 4.0 (Design)](https://developer.apple.com/app-store/review/guidelines/#design)
|
|
143
143
|
|
|
144
144
|
| Rule | Info.plist Key | What It Catches |
|
|
145
145
|
|------|---------------|-----------------|
|
|
146
|
-
| `missing-supported-orientations` | `UISupportedInterfaceOrientations` | Missing interface orientation declaration. Apps should explicitly declare which orientations they support to avoid UI issues on different devices. |
|
|
146
|
+
| `metadata-002-missing-supported-orientations` | `UISupportedInterfaceOrientations` | Missing interface orientation declaration. Apps should explicitly declare which orientations they support to avoid UI issues on different devices. |
|
|
147
147
|
|
|
148
148
|
---
|
|
149
149
|
|
|
@@ -176,6 +176,76 @@ shiplint scan ./MyApp --format sarif
|
|
|
176
176
|
|
|
177
177
|
---
|
|
178
178
|
|
|
179
|
+
## MCP Integration (AI Agents)
|
|
180
|
+
|
|
181
|
+
ShipLint includes a Model Context Protocol (MCP) server, letting AI coding assistants scan your project directly.
|
|
182
|
+
|
|
183
|
+
### Claude Code
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Add ShipLint as an MCP server
|
|
187
|
+
claude mcp add shiplint -- npx shiplint mcp
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Then just ask Claude:
|
|
191
|
+
> "Are there any App Store issues I should fix before submitting?"
|
|
192
|
+
|
|
193
|
+
Claude will call ShipLint, find issues, and can fix them automatically.
|
|
194
|
+
|
|
195
|
+
### Cursor / Windsurf
|
|
196
|
+
|
|
197
|
+
Add to your MCP settings (`.cursor/mcp.json` or equivalent):
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"mcpServers": {
|
|
202
|
+
"shiplint": {
|
|
203
|
+
"command": "npx",
|
|
204
|
+
"args": ["shiplint", "mcp"]
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Xcode 26.3 (Native MCP Support)
|
|
211
|
+
|
|
212
|
+
Xcode 26.3 includes native support for MCP servers in the Intelligence panel. Add ShipLint to your Claude Agent or Codex config:
|
|
213
|
+
|
|
214
|
+
**Config location:** `~/Library/Developer/Xcode/CodingAssistant/ClaudeAgentConfig`
|
|
215
|
+
|
|
216
|
+
```toml
|
|
217
|
+
[mcp_servers.shiplint]
|
|
218
|
+
command = "/bin/zsh"
|
|
219
|
+
args = ["-lc", "npx shiplint mcp"]
|
|
220
|
+
enabled = true
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Then ask Xcode's AI: "Scan this project for App Store issues."
|
|
224
|
+
|
|
225
|
+
### MCP Tools
|
|
226
|
+
|
|
227
|
+
ShipLint exposes three tools via MCP:
|
|
228
|
+
|
|
229
|
+
| Tool | Description |
|
|
230
|
+
|------|-------------|
|
|
231
|
+
| `shiplint_scan` | Scan a project for App Store issues |
|
|
232
|
+
| `shiplint_rules` | List all available rules |
|
|
233
|
+
| `shiplint_explain` | Explain a specific error code |
|
|
234
|
+
|
|
235
|
+
### Example Conversation
|
|
236
|
+
|
|
237
|
+
**You:** "Any issues before I submit to the App Store?"
|
|
238
|
+
|
|
239
|
+
**AI:** *Calls shiplint_scan* "Found 2 issues:
|
|
240
|
+
1. Missing `NSCameraUsageDescription` in Info.plist
|
|
241
|
+
2. No privacy manifest for UserDefaults API
|
|
242
|
+
|
|
243
|
+
I'll fix these now..."
|
|
244
|
+
|
|
245
|
+
**AI:** *Edits files* "Done. Added camera usage description and created PrivacyInfo.xcprivacy. Re-scan shows all checks passing. Ready to submit."
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
179
249
|
## CI/CD Integration
|
|
180
250
|
|
|
181
251
|
### GitHub Actions
|
|
@@ -240,9 +310,9 @@ Yes — and this is one of ShipLint's most valuable use cases. AI code generatio
|
|
|
240
310
|
|
|
241
311
|
ShipLint's rules are designed to prevent the most common automated rejection errors from App Store Connect:
|
|
242
312
|
|
|
243
|
-
- **ITMS-90683** — Missing purpose string (`NSCameraUsageDescription`, `NSMicrophoneUsageDescription`, `NSLocationWhenInUseUsageDescription`, `NSPhotoLibraryUsageDescription`, `NSContactsUsageDescription`, `NSUserTrackingUsageDescription`). Prevented by ShipLint's
|
|
244
|
-
- **ITMS-91053** — Missing privacy manifest (`PrivacyInfo.xcprivacy`). Prevented by the `missing-privacy-manifest` rule.
|
|
245
|
-
- **ITMS-90078** — Missing or misconfigured entitlements. Prevented by the `third-party-login-no-siwa` rule.
|
|
313
|
+
- **ITMS-90683** — Missing purpose string (`NSCameraUsageDescription`, `NSMicrophoneUsageDescription`, `NSLocationWhenInUseUsageDescription`, `NSPhotoLibraryUsageDescription`, `NSContactsUsageDescription`, `NSUserTrackingUsageDescription`). Prevented by ShipLint's `privacy-00x-missing-*-purpose` rules and the `privacy-003-att-tracking-mismatch` rule.
|
|
314
|
+
- **ITMS-91053** — Missing privacy manifest (`PrivacyInfo.xcprivacy`). Prevented by the `metadata-001-missing-privacy-manifest` rule.
|
|
315
|
+
- **ITMS-90078** — Missing or misconfigured entitlements. Prevented by the `auth-001-third-party-login-no-siwa` rule.
|
|
246
316
|
|
|
247
317
|
ShipLint also catches issues that trigger human reviewer rejections under Guidelines [2.1](https://developer.apple.com/app-store/review/guidelines/#performance), [4.8](https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple), and [5.1.1](https://developer.apple.com/app-store/review/guidelines/#data-collection-and-storage).
|
|
248
318
|
|
|
@@ -285,6 +355,32 @@ Different tools cover different layers of the submission process. Here's where S
|
|
|
285
355
|
|
|
286
356
|
---
|
|
287
357
|
|
|
358
|
+
## Analytics
|
|
359
|
+
|
|
360
|
+
ShipLint collects anonymous usage statistics to help improve the tool:
|
|
361
|
+
|
|
362
|
+
- CLI version
|
|
363
|
+
- Number of issues found (counts only)
|
|
364
|
+
- Which rules triggered (rule IDs only)
|
|
365
|
+
|
|
366
|
+
**No personal data, project names, or file contents are ever collected.**
|
|
367
|
+
|
|
368
|
+
### Opt-out
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
# Disable telemetry for a single run
|
|
372
|
+
SHIPLINT_NO_TELEMETRY=1 npx shiplint scan .
|
|
373
|
+
|
|
374
|
+
# Disable permanently (add to your shell profile)
|
|
375
|
+
export SHIPLINT_NO_TELEMETRY=1
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Public Stats
|
|
379
|
+
|
|
380
|
+
View aggregate usage stats at [shiplint.app/stats](https://shiplint.app/stats).
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
288
384
|
## Requirements
|
|
289
385
|
|
|
290
386
|
- **Node.js** 18 or later
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShipLint Analytics - Pieter Levels style
|
|
3
|
+
*
|
|
4
|
+
* Simple, anonymous, fire-and-forget telemetry.
|
|
5
|
+
* - No personal data
|
|
6
|
+
* - No project names
|
|
7
|
+
* - Just aggregate counts for public stats
|
|
8
|
+
*
|
|
9
|
+
* Opt-out: Set SHIPLINT_NO_TELEMETRY=1
|
|
10
|
+
*/
|
|
11
|
+
export type ProjectType = 'xcodeproj' | 'swiftpm' | 'both' | 'unknown';
|
|
12
|
+
export type FrameworkDetectionMethod = 'pbxproj' | 'import-scan' | 'both';
|
|
13
|
+
export type ScanMode = 'cli' | 'mcp';
|
|
14
|
+
export interface AnalyticsPayload {
|
|
15
|
+
v: string;
|
|
16
|
+
findings: number;
|
|
17
|
+
errors: number;
|
|
18
|
+
warnings: number;
|
|
19
|
+
rules: string[];
|
|
20
|
+
ts: number;
|
|
21
|
+
projectType: ProjectType;
|
|
22
|
+
frameworkDetectionMethod: FrameworkDetectionMethod;
|
|
23
|
+
frameworksDetected: string[];
|
|
24
|
+
targetCount: number;
|
|
25
|
+
scanMode: ScanMode;
|
|
26
|
+
findingsByRule: Record<string, number>;
|
|
27
|
+
totalFindings: number;
|
|
28
|
+
scanDurationMs: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Context for building enhanced analytics payload
|
|
32
|
+
*/
|
|
33
|
+
export interface AnalyticsContext {
|
|
34
|
+
version: string;
|
|
35
|
+
findings: Array<{
|
|
36
|
+
severity: string;
|
|
37
|
+
ruleId: string;
|
|
38
|
+
}>;
|
|
39
|
+
scanDurationMs: number;
|
|
40
|
+
scanMode: ScanMode;
|
|
41
|
+
projectType: ProjectType;
|
|
42
|
+
frameworkDetectionMethod: FrameworkDetectionMethod;
|
|
43
|
+
frameworksDetected: string[];
|
|
44
|
+
targetCount: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Send anonymous analytics ping (fire-and-forget)
|
|
48
|
+
* Non-blocking, silent failures
|
|
49
|
+
*/
|
|
50
|
+
export declare function ping(payload: AnalyticsPayload): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Build payload from scan results (legacy compat - defaults to cli mode with minimal context)
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildPayload(version: string, findings: Array<{
|
|
55
|
+
severity: string;
|
|
56
|
+
ruleId: string;
|
|
57
|
+
}>): AnalyticsPayload;
|
|
58
|
+
/**
|
|
59
|
+
* Build enhanced payload with full telemetry context
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildEnhancedPayload(ctx: AnalyticsContext): AnalyticsPayload;
|
|
62
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/cli/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AACvE,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;AAC1E,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;AAErC,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IAEX,WAAW,EAAE,WAAW,CAAC;IACzB,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBnE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GACpD,gBAAgB,CAWlB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,GAAG,gBAAgB,CA4B5E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ShipLint Analytics - Pieter Levels style
|
|
4
|
+
*
|
|
5
|
+
* Simple, anonymous, fire-and-forget telemetry.
|
|
6
|
+
* - No personal data
|
|
7
|
+
* - No project names
|
|
8
|
+
* - Just aggregate counts for public stats
|
|
9
|
+
*
|
|
10
|
+
* Opt-out: Set SHIPLINT_NO_TELEMETRY=1
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.ping = ping;
|
|
14
|
+
exports.buildPayload = buildPayload;
|
|
15
|
+
exports.buildEnhancedPayload = buildEnhancedPayload;
|
|
16
|
+
const ANALYTICS_ENDPOINT = 'https://shiplint.app/api/ping';
|
|
17
|
+
/**
|
|
18
|
+
* Send anonymous analytics ping (fire-and-forget)
|
|
19
|
+
* Non-blocking, silent failures
|
|
20
|
+
*/
|
|
21
|
+
async function ping(payload) {
|
|
22
|
+
// Check opt-out
|
|
23
|
+
if (process.env.SHIPLINT_NO_TELEMETRY === '1') {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// Fire and forget - don't await, don't block
|
|
28
|
+
fetch(ANALYTICS_ENDPOINT, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { 'Content-Type': 'application/json' },
|
|
31
|
+
body: JSON.stringify(payload),
|
|
32
|
+
}).catch(() => {
|
|
33
|
+
// Silent fail - analytics should never break the tool
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Silent fail
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build payload from scan results (legacy compat - defaults to cli mode with minimal context)
|
|
42
|
+
*/
|
|
43
|
+
function buildPayload(version, findings) {
|
|
44
|
+
return buildEnhancedPayload({
|
|
45
|
+
version,
|
|
46
|
+
findings,
|
|
47
|
+
scanDurationMs: 0,
|
|
48
|
+
scanMode: 'cli',
|
|
49
|
+
projectType: 'unknown',
|
|
50
|
+
frameworkDetectionMethod: 'import-scan',
|
|
51
|
+
frameworksDetected: [],
|
|
52
|
+
targetCount: 0,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build enhanced payload with full telemetry context
|
|
57
|
+
*/
|
|
58
|
+
function buildEnhancedPayload(ctx) {
|
|
59
|
+
const { findings } = ctx;
|
|
60
|
+
const errors = findings.filter(f => f.severity === 'error' || f.severity === 'critical').length;
|
|
61
|
+
const warnings = findings.filter(f => f.severity === 'warning').length;
|
|
62
|
+
const rules = [...new Set(findings.map(f => f.ruleId))];
|
|
63
|
+
// Build findingsByRule breakdown
|
|
64
|
+
const findingsByRule = {};
|
|
65
|
+
for (const f of findings) {
|
|
66
|
+
findingsByRule[f.ruleId] = (findingsByRule[f.ruleId] || 0) + 1;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
v: ctx.version,
|
|
70
|
+
findings: findings.length,
|
|
71
|
+
errors,
|
|
72
|
+
warnings,
|
|
73
|
+
rules,
|
|
74
|
+
ts: Date.now(),
|
|
75
|
+
projectType: ctx.projectType,
|
|
76
|
+
frameworkDetectionMethod: ctx.frameworkDetectionMethod,
|
|
77
|
+
frameworksDetected: ctx.frameworksDetected,
|
|
78
|
+
targetCount: ctx.targetCount,
|
|
79
|
+
scanMode: ctx.scanMode,
|
|
80
|
+
findingsByRule,
|
|
81
|
+
totalFindings: findings.length,
|
|
82
|
+
scanDurationMs: ctx.scanDurationMs,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/cli/analytics.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AA4CH,oBAkBC;AAKD,oCAcC;AAKD,oDA4BC;AAhHD,MAAM,kBAAkB,GAAG,+BAA+B,CAAC;AAsC3D;;;GAGG;AACI,KAAK,UAAU,IAAI,CAAC,OAAyB;IAClD,gBAAgB;IAChB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,EAAE,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,6CAA6C;QAC7C,KAAK,CAAC,kBAAkB,EAAE;YACxB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACZ,sDAAsD;QACxD,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,QAAqD;IAErD,OAAO,oBAAoB,CAAC;QAC1B,OAAO;QACP,QAAQ;QACR,cAAc,EAAE,CAAC;QACjB,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,SAAS;QACtB,wBAAwB,EAAE,aAAa;QACvC,kBAAkB,EAAE,EAAE;QACtB,WAAW,EAAE,CAAC;KACf,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAAC,GAAqB;IACxD,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;IAChG,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACvE,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAExD,iCAAiC;IACjC,MAAM,cAAc,GAA2B,EAAE,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACjE,CAAC;IAED,OAAO;QACL,CAAC,EAAE,GAAG,CAAC,OAAO;QACd,QAAQ,EAAE,QAAQ,CAAC,MAAM;QACzB,MAAM;QACN,QAAQ;QACR,KAAK;QACL,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;QACd,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,wBAAwB,EAAE,GAAG,CAAC,wBAAwB;QACtD,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;QAC1C,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,cAAc;QACd,aAAa,EAAE,QAAQ,CAAC,MAAM;QAC9B,cAAc,EAAE,GAAG,CAAC,cAAc;KACnC,CAAC;AACJ,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
3
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
7
|
/**
|
|
5
8
|
* ShipLint CLI
|
|
@@ -11,11 +14,14 @@ const scanner_js_1 = require("../core/scanner.js");
|
|
|
11
14
|
const index_js_1 = require("../formatters/index.js");
|
|
12
15
|
const index_js_2 = require("../rules/index.js");
|
|
13
16
|
const index_js_3 = require("../types/index.js");
|
|
17
|
+
const server_js_1 = require("../mcp/server.js");
|
|
18
|
+
const analytics_js_1 = require("./analytics.js");
|
|
19
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
14
20
|
const program = new commander_1.Command();
|
|
15
21
|
program
|
|
16
22
|
.name('shiplint')
|
|
17
23
|
.description('App Store Review Guideline scanner for iOS projects')
|
|
18
|
-
.version(
|
|
24
|
+
.version(package_json_1.default.version);
|
|
19
25
|
program
|
|
20
26
|
.command('scan')
|
|
21
27
|
.description('Scan an Xcode project for potential App Store Review issues')
|
|
@@ -36,6 +42,17 @@ program
|
|
|
36
42
|
});
|
|
37
43
|
const output = await (0, index_js_1.format)(result, outputFormat);
|
|
38
44
|
console.log(output);
|
|
45
|
+
// Anonymous analytics ping (fire-and-forget, opt-out with SHIPLINT_NO_TELEMETRY=1)
|
|
46
|
+
(0, analytics_js_1.ping)((0, analytics_js_1.buildEnhancedPayload)({
|
|
47
|
+
version: package_json_1.default.version,
|
|
48
|
+
findings: result.findings,
|
|
49
|
+
scanDurationMs: result.duration,
|
|
50
|
+
scanMode: 'cli',
|
|
51
|
+
projectType: result.projectType,
|
|
52
|
+
frameworkDetectionMethod: result.frameworkDetectionMethod,
|
|
53
|
+
frameworksDetected: result.frameworksDetected,
|
|
54
|
+
targetCount: result.targetCount,
|
|
55
|
+
}));
|
|
39
56
|
// Exit with error code if critical issues found
|
|
40
57
|
const hasCritical = result.findings.some(f => f.severity === 'critical');
|
|
41
58
|
if (hasCritical) {
|
|
@@ -85,6 +102,23 @@ program
|
|
|
85
102
|
console.log('\n');
|
|
86
103
|
}
|
|
87
104
|
});
|
|
105
|
+
program
|
|
106
|
+
.command('mcp')
|
|
107
|
+
.description('Start MCP (Model Context Protocol) server for AI agent integration')
|
|
108
|
+
.action(async () => {
|
|
109
|
+
try {
|
|
110
|
+
await (0, server_js_1.startMcpServer)();
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (error instanceof Error) {
|
|
114
|
+
console.error(`Error: ${error.message}`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.error('An unknown error occurred');
|
|
118
|
+
}
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
88
122
|
function parseOutputFormat(format) {
|
|
89
123
|
switch (format.toLowerCase()) {
|
|
90
124
|
case 'text':
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/core/scanner.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAK7F;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IACvB,UAAU,EAAE,MAAM,EAAE;IAAS,YAAY,EAAE,MAAM,EAAE;gBAAnD,UAAU,EAAE,MAAM,EAAE,EAAS,YAAY,EAAE,MAAM,EAAE;CAOvE;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA0FpE;AAkCD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,WAAW,EACpB,KAAK,CAAC,EAAE,IAAI,EAAE,GACb,OAAO,CAAC,OAAO,EAAE,CAAC,CAUpB"}
|
package/dist/core/scanner.js
CHANGED
|
@@ -1,8 +1,46 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.NoRulesError = exports.InvalidRulesError = void 0;
|
|
4
37
|
exports.scan = scan;
|
|
5
38
|
exports.scanWithContext = scanWithContext;
|
|
39
|
+
/**
|
|
40
|
+
* Core scan engine
|
|
41
|
+
*/
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
6
44
|
const project_parser_js_1 = require("../parsers/project-parser.js");
|
|
7
45
|
const index_js_1 = require("../rules/index.js");
|
|
8
46
|
/**
|
|
@@ -68,6 +106,7 @@ async function scan(options) {
|
|
|
68
106
|
// Run all rules
|
|
69
107
|
const findings = [];
|
|
70
108
|
const rulesRun = [];
|
|
109
|
+
const ruleErrors = [];
|
|
71
110
|
for (const rule of rules) {
|
|
72
111
|
try {
|
|
73
112
|
const ruleFindings = await rule.evaluate(context);
|
|
@@ -75,20 +114,62 @@ async function scan(options) {
|
|
|
75
114
|
rulesRun.push(rule.id);
|
|
76
115
|
}
|
|
77
116
|
catch (error) {
|
|
117
|
+
ruleErrors.push(rule.id);
|
|
78
118
|
if (options.verbose) {
|
|
79
119
|
console.error(`Error running rule ${rule.id}:`, error);
|
|
80
120
|
}
|
|
81
121
|
}
|
|
82
122
|
}
|
|
123
|
+
if (ruleErrors.length > 0) {
|
|
124
|
+
console.warn(`⚠️ ${ruleErrors.length} rule(s) failed to run: ${ruleErrors.join(', ')}. Use --verbose for details.`);
|
|
125
|
+
}
|
|
83
126
|
const duration = Date.now() - startTime;
|
|
127
|
+
// Determine project type and framework detection method from discovery
|
|
128
|
+
const projectType = deriveProjectType(discovery);
|
|
129
|
+
const frameworkDetectionMethod = deriveFrameworkDetectionMethod(discovery, context);
|
|
130
|
+
const frameworksDetected = [...context.linkedFrameworks].sort();
|
|
131
|
+
const targetCount = rules.length > 0 ? 1 : 0; // Currently scans main target
|
|
84
132
|
return {
|
|
85
133
|
projectPath: options.path,
|
|
86
134
|
timestamp: new Date(),
|
|
87
135
|
findings,
|
|
88
136
|
rulesRun,
|
|
89
137
|
duration,
|
|
138
|
+
projectType,
|
|
139
|
+
frameworkDetectionMethod,
|
|
140
|
+
frameworksDetected,
|
|
141
|
+
targetCount,
|
|
90
142
|
};
|
|
91
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Derive project type from discovery info
|
|
146
|
+
*/
|
|
147
|
+
function deriveProjectType(discovery) {
|
|
148
|
+
const hasXcodeproj = !!discovery.pbxprojPath;
|
|
149
|
+
// Check for Package.swift in project directory
|
|
150
|
+
const basePath = discovery.projectScopeDir ?? discovery.projectPath;
|
|
151
|
+
const hasPackageSwift = fs.existsSync(path.join(basePath, 'Package.swift'));
|
|
152
|
+
if (hasXcodeproj && hasPackageSwift)
|
|
153
|
+
return 'both';
|
|
154
|
+
if (hasXcodeproj)
|
|
155
|
+
return 'xcodeproj';
|
|
156
|
+
if (hasPackageSwift)
|
|
157
|
+
return 'swiftpm';
|
|
158
|
+
return 'unknown';
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Derive how frameworks were detected
|
|
162
|
+
*/
|
|
163
|
+
function deriveFrameworkDetectionMethod(discovery, context) {
|
|
164
|
+
const hasPbxproj = !!discovery.pbxprojPath;
|
|
165
|
+
// We always run import-scan (scanSwiftImports) in createScanContext
|
|
166
|
+
const hasImportScan = context.linkedFrameworks.size > 0;
|
|
167
|
+
if (hasPbxproj && hasImportScan)
|
|
168
|
+
return 'both';
|
|
169
|
+
if (hasPbxproj)
|
|
170
|
+
return 'pbxproj';
|
|
171
|
+
return 'import-scan';
|
|
172
|
+
}
|
|
92
173
|
/**
|
|
93
174
|
* Run a scan with a pre-built context (for testing)
|
|
94
175
|
*/
|
package/dist/core/scanner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/formatters/sarif.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/formatters/sarif.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,mBAAmB,CAAC;AAsF7D;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0BtD"}
|
package/dist/formatters/sarif.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.formatSARIF = formatSARIF;
|
|
4
7
|
const index_js_1 = require("../types/index.js");
|
|
8
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
5
9
|
/**
|
|
6
10
|
* SARIF schema version
|
|
7
11
|
*/
|
|
@@ -88,7 +92,7 @@ function formatSARIF(result) {
|
|
|
88
92
|
tool: {
|
|
89
93
|
driver: {
|
|
90
94
|
name: 'ShipLint',
|
|
91
|
-
version:
|
|
95
|
+
version: package_json_1.default.version,
|
|
92
96
|
informationUri: 'https://github.com/Signal26AI/ShipLint',
|
|
93
97
|
rules: buildRules(result.findings),
|
|
94
98
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/formatters/sarif.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/formatters/sarif.ts"],"names":[],"mappings":"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"}
|