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