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