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
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# ReviewShield
|
|
2
|
+
|
|
3
|
+
🛡️ Catch App Store rejections before they happen.
|
|
4
|
+
|
|
5
|
+
ReviewShield scans your iOS project for App Store Review Guideline violations and tells you exactly how to fix them.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g reviewshield
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or with npx (no install required):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx reviewshield scan ./MyApp.xcodeproj
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## The Problem
|
|
20
|
+
|
|
21
|
+
App Store rejections cost time and money:
|
|
22
|
+
- 2-7 day review delays
|
|
23
|
+
- Missed launch windows
|
|
24
|
+
- Frustrated users waiting for updates
|
|
25
|
+
|
|
26
|
+
Common culprits: missing privacy descriptions, Sign in with Apple requirements, tracking compliance — all preventable.
|
|
27
|
+
|
|
28
|
+
## The Solution
|
|
29
|
+
|
|
30
|
+
ReviewShield catches these issues **before** you submit:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
$ reviewshield scan ./MyApp.xcodeproj
|
|
34
|
+
|
|
35
|
+
🛡️ ReviewShield Scan Results
|
|
36
|
+
|
|
37
|
+
🔍 Found 2 issue(s):
|
|
38
|
+
|
|
39
|
+
1. [CRITICAL] Missing Camera Usage Description
|
|
40
|
+
📍 Info.plist • 📋 Guideline 5.1.1
|
|
41
|
+
|
|
42
|
+
Your app uses AVFoundation but Info.plist is missing
|
|
43
|
+
NSCameraUsageDescription...
|
|
44
|
+
|
|
45
|
+
How to fix:
|
|
46
|
+
Add NSCameraUsageDescription to your Info.plist...
|
|
47
|
+
|
|
48
|
+
2. [CRITICAL] Third-Party Login Without Sign in with Apple
|
|
49
|
+
📍 Entitlements • 📋 Guideline 4.8
|
|
50
|
+
|
|
51
|
+
Your app includes Google Sign-In but Sign in with Apple
|
|
52
|
+
is not configured...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## What We Check
|
|
56
|
+
|
|
57
|
+
| Category | Rules |
|
|
58
|
+
|----------|-------|
|
|
59
|
+
| **Privacy** | Camera, Location, Microphone, Photos, Contacts usage descriptions |
|
|
60
|
+
| **Tracking** | ATT compliance, tracking SDK detection |
|
|
61
|
+
| **Authentication** | Sign in with Apple requirements |
|
|
62
|
+
| **Security** | App Transport Security, Privacy Manifests (iOS 17+) |
|
|
63
|
+
|
|
64
|
+
10 rules today, more coming weekly.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Scan an Xcode project
|
|
70
|
+
reviewshield scan ./MyApp.xcodeproj
|
|
71
|
+
|
|
72
|
+
# Scan a directory
|
|
73
|
+
reviewshield scan ./ios
|
|
74
|
+
|
|
75
|
+
# Output as JSON
|
|
76
|
+
reviewshield scan ./MyApp.xcodeproj --format json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Rules (10 and growing)
|
|
80
|
+
|
|
81
|
+
**Privacy (Guideline 5.1.1)**
|
|
82
|
+
- `missing-camera-purpose` - Camera usage without NSCameraUsageDescription
|
|
83
|
+
- `missing-microphone-purpose` - Microphone usage without NSMicrophoneUsageDescription
|
|
84
|
+
- `missing-location-purpose` - Location usage without NSLocationUsageDescription
|
|
85
|
+
- `missing-photo-library-purpose` - Photo library usage without NSPhotoLibraryUsageDescription
|
|
86
|
+
- `missing-contacts-purpose` - Contacts usage without NSContactsUsageDescription
|
|
87
|
+
- `location-always-unjustified` - "Always" location without proper justification
|
|
88
|
+
- `att-tracking-mismatch` - ATT framework without NSUserTrackingUsageDescription
|
|
89
|
+
|
|
90
|
+
**Authentication (Guideline 4.8)**
|
|
91
|
+
- `third-party-login-no-siwa` - Third-party login without Sign in with Apple
|
|
92
|
+
|
|
93
|
+
**Security (Guideline 2.1)**
|
|
94
|
+
- `ats-exception-without-justification` - App Transport Security exceptions
|
|
95
|
+
|
|
96
|
+
**Metadata (iOS 17+)**
|
|
97
|
+
- `missing-privacy-manifest` - Missing PrivacyInfo.xcprivacy for required APIs
|
|
98
|
+
|
|
99
|
+
## Coming Soon
|
|
100
|
+
|
|
101
|
+
**ReviewShield GitHub App** — automatic PR checks, no setup required.
|
|
102
|
+
|
|
103
|
+
Join the waitlist: [reviewshield.dev](https://reviewshield.dev)
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
© 2026 Signal26. All rights reserved.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
/**
|
|
5
|
+
* ReviewShield CLI
|
|
6
|
+
*
|
|
7
|
+
* App Store Review Guideline scanner for iOS projects
|
|
8
|
+
*/
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const scanner_js_1 = require("../core/scanner.js");
|
|
11
|
+
const index_js_1 = require("../formatters/index.js");
|
|
12
|
+
const index_js_2 = require("../rules/index.js");
|
|
13
|
+
const index_js_3 = require("../types/index.js");
|
|
14
|
+
const program = new commander_1.Command();
|
|
15
|
+
program
|
|
16
|
+
.name('reviewshield')
|
|
17
|
+
.description('App Store Review Guideline scanner for iOS projects')
|
|
18
|
+
.version('0.1.0');
|
|
19
|
+
program
|
|
20
|
+
.command('scan')
|
|
21
|
+
.description('Scan an Xcode project for potential App Store Review issues')
|
|
22
|
+
.argument('<path>', 'Path to Xcode project, workspace, or directory')
|
|
23
|
+
.option('-f, --format <format>', 'Output format: text, json, sarif', 'text')
|
|
24
|
+
.option('-v, --verbose', 'Show verbose output', false)
|
|
25
|
+
.option('-r, --rules <rules...>', 'Only run specific rules (by ID)')
|
|
26
|
+
.option('-e, --exclude <rules...>', 'Exclude specific rules (by ID)')
|
|
27
|
+
.action(async (path, options) => {
|
|
28
|
+
try {
|
|
29
|
+
const outputFormat = parseOutputFormat(options.format);
|
|
30
|
+
const result = await (0, scanner_js_1.scan)({
|
|
31
|
+
path,
|
|
32
|
+
format: outputFormat,
|
|
33
|
+
verbose: options.verbose,
|
|
34
|
+
rules: options.rules,
|
|
35
|
+
exclude: options.exclude,
|
|
36
|
+
});
|
|
37
|
+
const output = await (0, index_js_1.format)(result, outputFormat);
|
|
38
|
+
console.log(output);
|
|
39
|
+
// Exit with error code if critical issues found
|
|
40
|
+
const hasCritical = result.findings.some(f => f.severity === 'critical');
|
|
41
|
+
if (hasCritical) {
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error instanceof Error) {
|
|
47
|
+
console.error(`Error: ${error.message}`);
|
|
48
|
+
if (options.verbose) {
|
|
49
|
+
console.error(error.stack);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.error('An unknown error occurred');
|
|
54
|
+
}
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
program
|
|
59
|
+
.command('rules')
|
|
60
|
+
.description('List all available rules')
|
|
61
|
+
.option('-f, --format <format>', 'Output format: text, json', 'text')
|
|
62
|
+
.action((options) => {
|
|
63
|
+
if (options.format === 'json') {
|
|
64
|
+
const rules = index_js_2.allRules.map(rule => ({
|
|
65
|
+
id: rule.id,
|
|
66
|
+
name: rule.name,
|
|
67
|
+
description: rule.description,
|
|
68
|
+
category: rule.category,
|
|
69
|
+
severity: rule.severity,
|
|
70
|
+
guideline: rule.guidelineReference,
|
|
71
|
+
}));
|
|
72
|
+
console.log(JSON.stringify(rules, null, 2));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log('\n🛡️ ReviewShield Rules\n');
|
|
76
|
+
console.log('='.repeat(60));
|
|
77
|
+
for (const rule of index_js_2.allRules) {
|
|
78
|
+
console.log(`\n${rule.id}`);
|
|
79
|
+
console.log(` Name: ${rule.name}`);
|
|
80
|
+
console.log(` Category: ${rule.category}`);
|
|
81
|
+
console.log(` Severity: ${rule.severity}`);
|
|
82
|
+
console.log(` Guideline: ${rule.guidelineReference}`);
|
|
83
|
+
console.log(` Description: ${rule.description}`);
|
|
84
|
+
}
|
|
85
|
+
console.log('\n');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
function parseOutputFormat(format) {
|
|
89
|
+
switch (format.toLowerCase()) {
|
|
90
|
+
case 'text':
|
|
91
|
+
return index_js_3.OutputFormat.Text;
|
|
92
|
+
case 'json':
|
|
93
|
+
return index_js_3.OutputFormat.JSON;
|
|
94
|
+
case 'sarif':
|
|
95
|
+
return index_js_3.OutputFormat.SARIF;
|
|
96
|
+
default:
|
|
97
|
+
throw new Error(`Unknown output format: ${format}. Use text, json, or sarif.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
program.parse();
|
|
101
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* Core module exports
|
|
19
|
+
*/
|
|
20
|
+
__exportStar(require("./scanner.js"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,+CAA6B"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core scan engine
|
|
3
|
+
*/
|
|
4
|
+
import type { Rule, Finding, ScanResult, ScanContext, ScanOptions } from '../types/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Error thrown when invalid rule IDs are specified
|
|
7
|
+
*/
|
|
8
|
+
export declare class InvalidRulesError extends Error {
|
|
9
|
+
unknownIds: string[];
|
|
10
|
+
availableIds: string[];
|
|
11
|
+
constructor(unknownIds: string[], availableIds: string[]);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Error thrown when no rules would be run
|
|
15
|
+
*/
|
|
16
|
+
export declare class NoRulesError extends Error {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Run a scan on the given path
|
|
21
|
+
*/
|
|
22
|
+
export declare function scan(options: ScanOptions): Promise<ScanResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Run a scan with a pre-built context (for testing)
|
|
25
|
+
*/
|
|
26
|
+
export declare function scanWithContext(context: ScanContext, rules?: Rule[]): Promise<Finding[]>;
|
|
27
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAI7F;;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,CA0EpE;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,WAAW,EACpB,KAAK,CAAC,EAAE,IAAI,EAAE,GACb,OAAO,CAAC,OAAO,EAAE,CAAC,CAUpB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoRulesError = exports.InvalidRulesError = void 0;
|
|
4
|
+
exports.scan = scan;
|
|
5
|
+
exports.scanWithContext = scanWithContext;
|
|
6
|
+
const project_parser_js_1 = require("../parsers/project-parser.js");
|
|
7
|
+
const index_js_1 = require("../rules/index.js");
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when invalid rule IDs are specified
|
|
10
|
+
*/
|
|
11
|
+
class InvalidRulesError extends Error {
|
|
12
|
+
unknownIds;
|
|
13
|
+
availableIds;
|
|
14
|
+
constructor(unknownIds, availableIds) {
|
|
15
|
+
super(`Unknown rule ID(s): ${unknownIds.join(', ')}. ` +
|
|
16
|
+
`Available rules: ${availableIds.join(', ')}`);
|
|
17
|
+
this.unknownIds = unknownIds;
|
|
18
|
+
this.availableIds = availableIds;
|
|
19
|
+
this.name = 'InvalidRulesError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.InvalidRulesError = InvalidRulesError;
|
|
23
|
+
/**
|
|
24
|
+
* Error thrown when no rules would be run
|
|
25
|
+
*/
|
|
26
|
+
class NoRulesError extends Error {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'NoRulesError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.NoRulesError = NoRulesError;
|
|
33
|
+
/**
|
|
34
|
+
* Run a scan on the given path
|
|
35
|
+
*/
|
|
36
|
+
async function scan(options) {
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
// Discover project structure
|
|
39
|
+
const discovery = (0, project_parser_js_1.discoverProject)(options.path);
|
|
40
|
+
// Create scan context
|
|
41
|
+
const context = (0, project_parser_js_1.createScanContext)(discovery);
|
|
42
|
+
// Determine which rules to run
|
|
43
|
+
let rules;
|
|
44
|
+
if (options.rules && options.rules.length > 0) {
|
|
45
|
+
// BUG FIX #2: Validate rule IDs and error on unknown
|
|
46
|
+
const { rules: foundRules, unknownIds } = (0, index_js_1.getRulesWithValidation)(options.rules);
|
|
47
|
+
if (unknownIds.length > 0) {
|
|
48
|
+
throw new InvalidRulesError(unknownIds, index_js_1.allRules.map(r => r.id));
|
|
49
|
+
}
|
|
50
|
+
if (foundRules.length === 0) {
|
|
51
|
+
throw new NoRulesError('No valid rules to run. Check your --rules argument.');
|
|
52
|
+
}
|
|
53
|
+
rules = foundRules;
|
|
54
|
+
}
|
|
55
|
+
else if (options.exclude && options.exclude.length > 0) {
|
|
56
|
+
rules = (0, index_js_1.getRulesExcluding)(options.exclude);
|
|
57
|
+
if (rules.length === 0) {
|
|
58
|
+
throw new NoRulesError('All rules were excluded. At least one rule must run.');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
rules = index_js_1.allRules;
|
|
63
|
+
}
|
|
64
|
+
// Final safety check: never run with zero rules
|
|
65
|
+
if (rules.length === 0) {
|
|
66
|
+
throw new NoRulesError('No rules available to run. This should not happen - please report this bug.');
|
|
67
|
+
}
|
|
68
|
+
// Run all rules
|
|
69
|
+
const findings = [];
|
|
70
|
+
const rulesRun = [];
|
|
71
|
+
for (const rule of rules) {
|
|
72
|
+
try {
|
|
73
|
+
const ruleFindings = await rule.evaluate(context);
|
|
74
|
+
findings.push(...ruleFindings);
|
|
75
|
+
rulesRun.push(rule.id);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (options.verbose) {
|
|
79
|
+
console.error(`Error running rule ${rule.id}:`, error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const duration = Date.now() - startTime;
|
|
84
|
+
return {
|
|
85
|
+
projectPath: options.path,
|
|
86
|
+
timestamp: new Date(),
|
|
87
|
+
findings,
|
|
88
|
+
rulesRun,
|
|
89
|
+
duration,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Run a scan with a pre-built context (for testing)
|
|
94
|
+
*/
|
|
95
|
+
async function scanWithContext(context, rules) {
|
|
96
|
+
const rulesToRun = rules ?? index_js_1.allRules;
|
|
97
|
+
const findings = [];
|
|
98
|
+
for (const rule of rulesToRun) {
|
|
99
|
+
const ruleFindings = await rule.evaluate(context);
|
|
100
|
+
findings.push(...ruleFindings);
|
|
101
|
+
}
|
|
102
|
+
return findings;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":";;;AAiCA,oBA0EC;AAKD,0CAaC;AAzHD,oEAAkF;AAClF,gDAAwF;AAExF;;GAEG;AACH,MAAa,iBAAkB,SAAQ,KAAK;IACvB;IAA6B;IAAhD,YAAmB,UAAoB,EAAS,YAAsB;QACpE,KAAK,CACH,uBAAuB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAChD,oBAAoB,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9C,CAAC;QAJe,eAAU,GAAV,UAAU,CAAU;QAAS,iBAAY,GAAZ,YAAY,CAAU;QAKpE,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AARD,8CAQC;AAED;;GAEG;AACH,MAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AALD,oCAKC;AAED;;GAEG;AACI,KAAK,UAAU,IAAI,CAAC,OAAoB;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,6BAA6B;IAC7B,MAAM,SAAS,GAAG,IAAA,mCAAe,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,sBAAsB;IACtB,MAAM,OAAO,GAAG,IAAA,qCAAiB,EAAC,SAAS,CAAC,CAAC;IAE7C,+BAA+B;IAC/B,IAAI,KAAa,CAAC;IAElB,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,qDAAqD;QACrD,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,IAAA,iCAAsB,EAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEhF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,iBAAiB,CACzB,UAAU,EACV,mBAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACxB,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,YAAY,CACpB,qDAAqD,CACtD,CAAC;QACJ,CAAC;QAED,KAAK,GAAG,UAAU,CAAC;IACrB,CAAC;SAAM,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,KAAK,GAAG,IAAA,4BAAiB,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAE3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,YAAY,CACpB,sDAAsD,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,mBAAQ,CAAC;IACnB,CAAC;IAED,gDAAgD;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,YAAY,CACpB,6EAA6E,CAC9E,CAAC;IACJ,CAAC;IAED,gBAAgB;IAChB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,CAAC,KAAK,CAAC,sBAAsB,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IAExC,OAAO;QACL,WAAW,EAAE,OAAO,CAAC,IAAI;QACzB,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,QAAQ;QACR,QAAQ;QACR,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,eAAe,CACnC,OAAoB,EACpB,KAAc;IAEd,MAAM,UAAU,GAAG,KAAK,IAAI,mBAAQ,CAAC;IACrC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClD,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatters module exports
|
|
3
|
+
*/
|
|
4
|
+
import type { ScanResult } from '../types/index.js';
|
|
5
|
+
import { OutputFormat } from '../types/index.js';
|
|
6
|
+
export { formatText } from './text.js';
|
|
7
|
+
export { formatJSON, formatJSONCompact } from './json.js';
|
|
8
|
+
export { formatSARIF } from './sarif.js';
|
|
9
|
+
/**
|
|
10
|
+
* Format scan results based on output format
|
|
11
|
+
*/
|
|
12
|
+
export declare function format(result: ScanResult, outputFormat: OutputFormat): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formatters/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAKjD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC;;GAEG;AACH,wBAAsB,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5F"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatSARIF = exports.formatJSONCompact = exports.formatJSON = exports.formatText = void 0;
|
|
4
|
+
exports.format = format;
|
|
5
|
+
const index_js_1 = require("../types/index.js");
|
|
6
|
+
const text_js_1 = require("./text.js");
|
|
7
|
+
const json_js_1 = require("./json.js");
|
|
8
|
+
const sarif_js_1 = require("./sarif.js");
|
|
9
|
+
var text_js_2 = require("./text.js");
|
|
10
|
+
Object.defineProperty(exports, "formatText", { enumerable: true, get: function () { return text_js_2.formatText; } });
|
|
11
|
+
var json_js_2 = require("./json.js");
|
|
12
|
+
Object.defineProperty(exports, "formatJSON", { enumerable: true, get: function () { return json_js_2.formatJSON; } });
|
|
13
|
+
Object.defineProperty(exports, "formatJSONCompact", { enumerable: true, get: function () { return json_js_2.formatJSONCompact; } });
|
|
14
|
+
var sarif_js_2 = require("./sarif.js");
|
|
15
|
+
Object.defineProperty(exports, "formatSARIF", { enumerable: true, get: function () { return sarif_js_2.formatSARIF; } });
|
|
16
|
+
/**
|
|
17
|
+
* Format scan results based on output format
|
|
18
|
+
*/
|
|
19
|
+
async function format(result, outputFormat) {
|
|
20
|
+
switch (outputFormat) {
|
|
21
|
+
case index_js_1.OutputFormat.Text:
|
|
22
|
+
return (0, text_js_1.formatText)(result);
|
|
23
|
+
case index_js_1.OutputFormat.JSON:
|
|
24
|
+
return (0, json_js_1.formatJSON)(result);
|
|
25
|
+
case index_js_1.OutputFormat.SARIF:
|
|
26
|
+
return (0, sarif_js_1.formatSARIF)(result);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/formatters/index.ts"],"names":[],"mappings":";;;AAgBA,wBASC;AArBD,gDAAiD;AACjD,uCAAuC;AACvC,uCAAuC;AACvC,yCAAyC;AAEzC,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AACnB,qCAA0D;AAAjD,qGAAA,UAAU,OAAA;AAAE,4GAAA,iBAAiB,OAAA;AACtC,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AAEpB;;GAEG;AACI,KAAK,UAAU,MAAM,CAAC,MAAkB,EAAE,YAA0B;IACzE,QAAQ,YAAY,EAAE,CAAC;QACrB,KAAK,uBAAY,CAAC,IAAI;YACpB,OAAO,IAAA,oBAAU,EAAC,MAAM,CAAC,CAAC;QAC5B,KAAK,uBAAY,CAAC,IAAI;YACpB,OAAO,IAAA,oBAAU,EAAC,MAAM,CAAC,CAAC;QAC5B,KAAK,uBAAY,CAAC,KAAK;YACrB,OAAO,IAAA,sBAAW,EAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON formatter for machine-readable output
|
|
3
|
+
*/
|
|
4
|
+
import type { ScanResult } from '../types/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Format scan results as JSON
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatJSON(result: ScanResult): string;
|
|
9
|
+
/**
|
|
10
|
+
* Format scan results as compact JSON (single line)
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatJSONCompact(result: ScanResult): string;
|
|
13
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/formatters/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatJSON = formatJSON;
|
|
4
|
+
exports.formatJSONCompact = formatJSONCompact;
|
|
5
|
+
/**
|
|
6
|
+
* Format scan results as JSON
|
|
7
|
+
*/
|
|
8
|
+
function formatJSON(result) {
|
|
9
|
+
return JSON.stringify(result, null, 2);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Format scan results as compact JSON (single line)
|
|
13
|
+
*/
|
|
14
|
+
function formatJSONCompact(result) {
|
|
15
|
+
return JSON.stringify(result);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/formatters/json.ts"],"names":[],"mappings":";;AAQA,gCAEC;AAKD,8CAEC;AAZD;;GAEG;AACH,SAAgB,UAAU,CAAC,MAAkB;IAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,MAAkB;IAClD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF formatter for CI/CD integration
|
|
3
|
+
*
|
|
4
|
+
* SARIF (Static Analysis Results Interchange Format) is a standard format
|
|
5
|
+
* for static analysis tools. It's supported by GitHub, Azure DevOps, and others.
|
|
6
|
+
*
|
|
7
|
+
* @see https://sarifweb.azurewebsites.net/
|
|
8
|
+
*/
|
|
9
|
+
import type { ScanResult } from '../types/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Format scan results as SARIF
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatSARIF(result: ScanResult): string;
|
|
14
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +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;AAqF7D;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA0BtD"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatSARIF = formatSARIF;
|
|
4
|
+
const index_js_1 = require("../types/index.js");
|
|
5
|
+
/**
|
|
6
|
+
* SARIF schema version
|
|
7
|
+
*/
|
|
8
|
+
const SARIF_VERSION = '2.1.0';
|
|
9
|
+
const SARIF_SCHEMA = 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json';
|
|
10
|
+
/**
|
|
11
|
+
* Convert severity to SARIF level
|
|
12
|
+
*/
|
|
13
|
+
function toSarifLevel(severity) {
|
|
14
|
+
switch (severity) {
|
|
15
|
+
case index_js_1.Severity.Critical:
|
|
16
|
+
case index_js_1.Severity.High:
|
|
17
|
+
return 'error';
|
|
18
|
+
case index_js_1.Severity.Medium:
|
|
19
|
+
return 'warning';
|
|
20
|
+
case index_js_1.Severity.Low:
|
|
21
|
+
case index_js_1.Severity.Info:
|
|
22
|
+
return 'note';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Convert finding to SARIF result
|
|
27
|
+
*/
|
|
28
|
+
function toSarifResult(finding) {
|
|
29
|
+
const result = {
|
|
30
|
+
ruleId: finding.ruleId,
|
|
31
|
+
level: toSarifLevel(finding.severity),
|
|
32
|
+
message: {
|
|
33
|
+
text: finding.description,
|
|
34
|
+
},
|
|
35
|
+
properties: {
|
|
36
|
+
confidence: finding.confidence,
|
|
37
|
+
guideline: finding.guideline,
|
|
38
|
+
fixGuidance: finding.fixGuidance,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
if (finding.location) {
|
|
42
|
+
result.locations = [
|
|
43
|
+
{
|
|
44
|
+
physicalLocation: {
|
|
45
|
+
artifactLocation: {
|
|
46
|
+
uri: finding.location,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build SARIF rules array from findings
|
|
56
|
+
*/
|
|
57
|
+
function buildRules(findings) {
|
|
58
|
+
const ruleMap = new Map();
|
|
59
|
+
for (const finding of findings) {
|
|
60
|
+
if (!ruleMap.has(finding.ruleId)) {
|
|
61
|
+
ruleMap.set(finding.ruleId, finding);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return Array.from(ruleMap.values()).map(finding => ({
|
|
65
|
+
id: finding.ruleId,
|
|
66
|
+
name: finding.title,
|
|
67
|
+
shortDescription: {
|
|
68
|
+
text: finding.title,
|
|
69
|
+
},
|
|
70
|
+
fullDescription: {
|
|
71
|
+
text: finding.description,
|
|
72
|
+
},
|
|
73
|
+
helpUri: finding.documentationURL,
|
|
74
|
+
properties: {
|
|
75
|
+
guideline: finding.guideline,
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Format scan results as SARIF
|
|
81
|
+
*/
|
|
82
|
+
function formatSARIF(result) {
|
|
83
|
+
const sarif = {
|
|
84
|
+
$schema: SARIF_SCHEMA,
|
|
85
|
+
version: SARIF_VERSION,
|
|
86
|
+
runs: [
|
|
87
|
+
{
|
|
88
|
+
tool: {
|
|
89
|
+
driver: {
|
|
90
|
+
name: 'ReviewShield',
|
|
91
|
+
version: '0.1.0',
|
|
92
|
+
informationUri: 'https://github.com/kazuochi/ReviewShield_iOS',
|
|
93
|
+
rules: buildRules(result.findings),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
results: result.findings.map(toSarifResult),
|
|
97
|
+
invocations: [
|
|
98
|
+
{
|
|
99
|
+
executionSuccessful: true,
|
|
100
|
+
endTimeUtc: result.timestamp.toISOString(),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
return JSON.stringify(sarif, null, 2);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/formatters/sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,mBAAmB,CAAC;AAyF7D;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuDpE"}
|