@sentinel-atl/scanner 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +104 -0
  2. package/dist/dependency-scanner.d.ts +22 -0
  3. package/dist/dependency-scanner.d.ts.map +1 -0
  4. package/dist/dependency-scanner.js +70 -0
  5. package/dist/dependency-scanner.js.map +1 -0
  6. package/dist/index.d.ts +24 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +24 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/package-resolver.d.ts +30 -0
  11. package/dist/package-resolver.d.ts.map +1 -0
  12. package/dist/package-resolver.js +105 -0
  13. package/dist/package-resolver.js.map +1 -0
  14. package/dist/pattern-scanner.d.ts +30 -0
  15. package/dist/pattern-scanner.d.ts.map +1 -0
  16. package/dist/pattern-scanner.js +178 -0
  17. package/dist/pattern-scanner.js.map +1 -0
  18. package/dist/permission-scanner.d.ts +28 -0
  19. package/dist/permission-scanner.d.ts.map +1 -0
  20. package/dist/permission-scanner.js +100 -0
  21. package/dist/permission-scanner.js.map +1 -0
  22. package/dist/publisher-scanner.d.ts +56 -0
  23. package/dist/publisher-scanner.d.ts.map +1 -0
  24. package/dist/publisher-scanner.js +238 -0
  25. package/dist/publisher-scanner.js.map +1 -0
  26. package/dist/scanner.d.ts +61 -0
  27. package/dist/scanner.d.ts.map +1 -0
  28. package/dist/scanner.js +71 -0
  29. package/dist/scanner.js.map +1 -0
  30. package/dist/stc.d.ts +88 -0
  31. package/dist/stc.d.ts.map +1 -0
  32. package/dist/stc.js +115 -0
  33. package/dist/stc.js.map +1 -0
  34. package/dist/tool-prober.d.ts +46 -0
  35. package/dist/tool-prober.d.ts.map +1 -0
  36. package/dist/tool-prober.js +158 -0
  37. package/dist/tool-prober.js.map +1 -0
  38. package/dist/trust-score.d.ts +26 -0
  39. package/dist/trust-score.d.ts.map +1 -0
  40. package/dist/trust-score.js +65 -0
  41. package/dist/trust-score.js.map +1 -0
  42. package/package.json +52 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Permission scanner — detects what system resources an MCP server accesses.
3
+ *
4
+ * Analyzes imports and API usage to determine permission scope:
5
+ * - filesystem: fs, path operations
6
+ * - network: http, https, net, tls, dns, fetch
7
+ * - process: child_process, exec, spawn
8
+ * - crypto: crypto operations (generally safe, but noted)
9
+ * - environment: process.env access
10
+ */
11
+ import type { Finding } from './scanner.js';
12
+ export type PermissionKind = 'filesystem' | 'network' | 'process' | 'crypto' | 'environment' | 'native';
13
+ export interface DetectedPermission {
14
+ kind: PermissionKind;
15
+ source: string;
16
+ file: string;
17
+ line: number;
18
+ evidence: string;
19
+ }
20
+ export interface PermissionScanResult {
21
+ /** Unique permission kinds detected */
22
+ kinds: PermissionKind[];
23
+ /** All detected permission usages */
24
+ detections: DetectedPermission[];
25
+ findings: Finding[];
26
+ }
27
+ export declare function scanPermissions(packagePath: string, extensions: string[]): Promise<PermissionScanResult>;
28
+ //# sourceMappingURL=permission-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-scanner.d.ts","sourceRoot":"","sources":["../src/permission-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,SAAS,GACT,SAAS,GACT,QAAQ,GACR,aAAa,GACb,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,qCAAqC;IACrC,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAwCD,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,oBAAoB,CAAC,CA6C/B"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Permission scanner — detects what system resources an MCP server accesses.
3
+ *
4
+ * Analyzes imports and API usage to determine permission scope:
5
+ * - filesystem: fs, path operations
6
+ * - network: http, https, net, tls, dns, fetch
7
+ * - process: child_process, exec, spawn
8
+ * - crypto: crypto operations (generally safe, but noted)
9
+ * - environment: process.env access
10
+ */
11
+ import { readFile, readdir } from 'node:fs/promises';
12
+ import { join, relative } from 'node:path';
13
+ const PERMISSION_RULES = [
14
+ // Filesystem
15
+ { kind: 'filesystem', pattern: /(?:from|require\s*\()\s*['"](?:node:)?fs(?:\/promises)?['"]/g, source: 'fs module', severity: 'medium' },
16
+ { kind: 'filesystem', pattern: /(?:readFile|writeFile|readdir|mkdir|rmdir|unlink|rename|copyFile|stat|access)\s*\(/g, source: 'fs operation', severity: 'medium' },
17
+ { kind: 'filesystem', pattern: /(?:createReadStream|createWriteStream)\s*\(/g, source: 'fs stream', severity: 'medium' },
18
+ // Network
19
+ { kind: 'network', pattern: /(?:from|require\s*\()\s*['"](?:node:)?(?:http|https|net|tls|dgram)['"]/g, source: 'network module', severity: 'high' },
20
+ { kind: 'network', pattern: /(?:from|require\s*\()\s*['"](?:node-fetch|axios|got|undici)['"]/g, source: 'http client library', severity: 'high' },
21
+ { kind: 'network', pattern: /\bfetch\s*\(/g, source: 'global fetch', severity: 'medium' },
22
+ // Process/Shell
23
+ { kind: 'process', pattern: /(?:from|require\s*\()\s*['"](?:node:)?child_process['"]/g, source: 'child_process module', severity: 'critical' },
24
+ { kind: 'process', pattern: /(?:exec|execFile|execSync|spawn|spawnSync|fork)\s*\(/g, source: 'process execution', severity: 'critical' },
25
+ { kind: 'process', pattern: /process\.(?:kill|exit|abort)\s*\(/g, source: 'process control', severity: 'high' },
26
+ // Crypto (generally safe but noted)
27
+ { kind: 'crypto', pattern: /(?:from|require\s*\()\s*['"](?:node:)?crypto['"]/g, source: 'crypto module', severity: 'info' },
28
+ // Environment
29
+ { kind: 'environment', pattern: /process\.env(?:\.|(?:\[))/g, source: 'environment variable', severity: 'low' },
30
+ // Native modules
31
+ { kind: 'native', pattern: /(?:from|require\s*\()\s*['"].*\.node['"]/g, source: 'native addon', severity: 'high' },
32
+ { kind: 'native', pattern: /(?:from|require\s*\()\s*['"](?:node:)?(?:v8|vm|worker_threads)['"]/g, source: 'low-level module', severity: 'high' },
33
+ ];
34
+ // ─── Scanner ─────────────────────────────────────────────────────────
35
+ export async function scanPermissions(packagePath, extensions) {
36
+ const files = await collectSourceFiles(packagePath, extensions);
37
+ const detections = [];
38
+ const findings = [];
39
+ for (const filePath of files) {
40
+ const content = await readFile(filePath, 'utf-8');
41
+ const lines = content.split('\n');
42
+ const relPath = relative(packagePath, filePath);
43
+ for (const rule of PERMISSION_RULES) {
44
+ for (let i = 0; i < lines.length; i++) {
45
+ const line = lines[i];
46
+ rule.pattern.lastIndex = 0;
47
+ if (rule.pattern.test(line)) {
48
+ const trimmed = line.trim();
49
+ if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) {
50
+ continue;
51
+ }
52
+ detections.push({
53
+ kind: rule.kind,
54
+ source: rule.source,
55
+ file: relPath,
56
+ line: i + 1,
57
+ evidence: trimmed.slice(0, 120),
58
+ });
59
+ findings.push({
60
+ severity: rule.severity,
61
+ category: 'permission',
62
+ title: `${rule.kind}: ${rule.source} in ${relPath}:${i + 1}`,
63
+ description: `Detected ${rule.kind} access via ${rule.source}`,
64
+ file: relPath,
65
+ line: i + 1,
66
+ evidence: trimmed.slice(0, 120),
67
+ });
68
+ }
69
+ }
70
+ }
71
+ }
72
+ const kinds = [...new Set(detections.map(d => d.kind))];
73
+ return { kinds, detections, findings };
74
+ }
75
+ // ─── File Collection ─────────────────────────────────────────────────
76
+ async function collectSourceFiles(dir, extensions, basePath) {
77
+ basePath ??= dir;
78
+ const files = [];
79
+ let entries;
80
+ try {
81
+ entries = await readdir(dir, { withFileTypes: true });
82
+ }
83
+ catch {
84
+ return files;
85
+ }
86
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
87
+ const fullPath = join(dir, entry.name);
88
+ if (entry.isDirectory()) {
89
+ if (['node_modules', 'dist', '.git', '.turbo', 'coverage', '__pycache__'].includes(entry.name)) {
90
+ continue;
91
+ }
92
+ files.push(...await collectSourceFiles(fullPath, extensions, basePath));
93
+ }
94
+ else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
95
+ files.push(fullPath);
96
+ }
97
+ }
98
+ return files;
99
+ }
100
+ //# sourceMappingURL=permission-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-scanner.js","sourceRoot":"","sources":["../src/permission-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Publisher verifier — checks npm registry for package publisher identity signals.
3
+ *
4
+ * Checks:
5
+ * 1. Package exists on npm registry
6
+ * 2. Publisher/maintainer count and identities
7
+ * 3. Whether the publisher has 2FA enabled (npm provenance)
8
+ * 4. Package age (how long has it existed?)
9
+ * 5. Download count (popularity signal)
10
+ * 6. Repository link presence and match
11
+ * 7. npm provenance attestation (sigstore)
12
+ */
13
+ import type { Finding } from './scanner.js';
14
+ export interface PublisherInfo {
15
+ /** npm package name */
16
+ packageName: string;
17
+ /** Whether the package exists on npm */
18
+ existsOnNpm: boolean;
19
+ /** npm publisher username */
20
+ publisher?: string;
21
+ /** npm publisher email */
22
+ publisherEmail?: string;
23
+ /** Number of maintainers */
24
+ maintainerCount: number;
25
+ /** Maintainer usernames */
26
+ maintainers: string[];
27
+ /** Package creation date */
28
+ createdAt?: string;
29
+ /** Last publish date */
30
+ lastPublishedAt?: string;
31
+ /** Package age in days */
32
+ ageDays: number;
33
+ /** Weekly downloads */
34
+ weeklyDownloads: number;
35
+ /** Whether the package has a repository link */
36
+ hasRepository: boolean;
37
+ /** Repository URL */
38
+ repositoryUrl?: string;
39
+ /** Whether npm provenance attestation is present */
40
+ hasProvenance: boolean;
41
+ /** License */
42
+ license?: string;
43
+ /** Number of published versions */
44
+ versionCount: number;
45
+ }
46
+ export interface PublisherScanResult {
47
+ info: PublisherInfo;
48
+ findings: Finding[];
49
+ /** Publisher trust score 0-100 */
50
+ score: number;
51
+ }
52
+ /**
53
+ * Verify publisher identity by checking the npm registry.
54
+ */
55
+ export declare function scanPublisher(packageName: string): Promise<PublisherScanResult>;
56
+ //# sourceMappingURL=publisher-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publisher-scanner.d.ts","sourceRoot":"","sources":["../src/publisher-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAI5C,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4BAA4B;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,2BAA2B;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;CACf;AAiCD;;GAEG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAiJrF"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Publisher verifier — checks npm registry for package publisher identity signals.
3
+ *
4
+ * Checks:
5
+ * 1. Package exists on npm registry
6
+ * 2. Publisher/maintainer count and identities
7
+ * 3. Whether the publisher has 2FA enabled (npm provenance)
8
+ * 4. Package age (how long has it existed?)
9
+ * 5. Download count (popularity signal)
10
+ * 6. Repository link presence and match
11
+ * 7. npm provenance attestation (sigstore)
12
+ */
13
+ // ─── Registry Fetcher ─────────────────────────────────────────────────
14
+ async function fetchRegistryData(packageName) {
15
+ const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
16
+ const response = await fetch(url, {
17
+ headers: { 'Accept': 'application/json' },
18
+ signal: AbortSignal.timeout(10_000),
19
+ });
20
+ if (!response.ok) {
21
+ if (response.status === 404)
22
+ return null;
23
+ throw new Error(`npm registry returned ${response.status}`);
24
+ }
25
+ return response.json();
26
+ }
27
+ async function fetchDownloadCount(packageName) {
28
+ try {
29
+ const url = `https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`;
30
+ const response = await fetch(url, {
31
+ signal: AbortSignal.timeout(5_000),
32
+ });
33
+ if (!response.ok)
34
+ return 0;
35
+ const data = await response.json();
36
+ return data.downloads ?? 0;
37
+ }
38
+ catch {
39
+ return 0;
40
+ }
41
+ }
42
+ // ─── Scanner ─────────────────────────────────────────────────────────
43
+ /**
44
+ * Verify publisher identity by checking the npm registry.
45
+ */
46
+ export async function scanPublisher(packageName) {
47
+ const findings = [];
48
+ // Skip for local-only / unknown packages
49
+ if (!packageName || packageName === 'unknown' || packageName.startsWith('/')) {
50
+ return {
51
+ info: emptyInfo(packageName),
52
+ findings,
53
+ score: 50, // neutral for local packages
54
+ };
55
+ }
56
+ try {
57
+ const [registryData, weeklyDownloads] = await Promise.all([
58
+ fetchRegistryData(packageName),
59
+ fetchDownloadCount(packageName),
60
+ ]);
61
+ if (!registryData) {
62
+ findings.push({
63
+ severity: 'medium',
64
+ category: 'publisher',
65
+ title: 'Package not found on npm',
66
+ description: `"${packageName}" does not exist on the npm registry`,
67
+ });
68
+ return { info: { ...emptyInfo(packageName), existsOnNpm: false }, findings, score: 20 };
69
+ }
70
+ // Extract publisher info
71
+ const timeData = registryData.time ?? {};
72
+ const createdAt = timeData.created;
73
+ const lastPublishedAt = timeData.modified;
74
+ const maintainers = registryData.maintainers ?? [];
75
+ const latestVersion = registryData['dist-tags']?.latest;
76
+ const latestData = latestVersion ? registryData.versions?.[latestVersion] : undefined;
77
+ const ageDays = createdAt
78
+ ? Math.floor((Date.now() - new Date(createdAt).getTime()) / 86_400_000)
79
+ : 0;
80
+ const repositoryUrl = latestData?.repository?.url ?? registryData.repository?.url;
81
+ const hasRepository = !!repositoryUrl;
82
+ const license = latestData?.license ?? registryData.license;
83
+ const hasProvenance = !!latestData?.dist?.attestations || !!latestData?.dist?.signatures?.length;
84
+ const versionCount = Object.keys(registryData.versions ?? {}).length;
85
+ // Determine publisher from _npmUser on latest version
86
+ const npmUser = latestData?._npmUser;
87
+ const info = {
88
+ packageName,
89
+ existsOnNpm: true,
90
+ publisher: npmUser?.name,
91
+ publisherEmail: npmUser?.email,
92
+ maintainerCount: maintainers.length,
93
+ maintainers: maintainers.map(m => m.name),
94
+ createdAt,
95
+ lastPublishedAt,
96
+ ageDays,
97
+ weeklyDownloads,
98
+ hasRepository,
99
+ repositoryUrl,
100
+ hasProvenance,
101
+ license,
102
+ versionCount,
103
+ };
104
+ // Generate findings
105
+ if (ageDays < 30) {
106
+ findings.push({
107
+ severity: 'medium',
108
+ category: 'publisher',
109
+ title: `New package: ${ageDays} days old`,
110
+ description: 'Package was published less than 30 days ago — limited track record',
111
+ });
112
+ }
113
+ if (weeklyDownloads < 100) {
114
+ findings.push({
115
+ severity: 'low',
116
+ category: 'publisher',
117
+ title: `Low popularity: ${weeklyDownloads} downloads/week`,
118
+ description: 'Low download count suggests limited community vetting',
119
+ });
120
+ }
121
+ if (maintainers.length === 1) {
122
+ findings.push({
123
+ severity: 'low',
124
+ category: 'publisher',
125
+ title: 'Single maintainer',
126
+ description: 'Package has only one maintainer — single point of failure for supply chain',
127
+ });
128
+ }
129
+ if (!hasRepository) {
130
+ findings.push({
131
+ severity: 'medium',
132
+ category: 'publisher',
133
+ title: 'No repository link',
134
+ description: 'Package does not link to a source code repository',
135
+ });
136
+ }
137
+ if (!license) {
138
+ findings.push({
139
+ severity: 'medium',
140
+ category: 'publisher',
141
+ title: 'No license specified',
142
+ description: 'Package does not specify a license',
143
+ });
144
+ }
145
+ if (!hasProvenance) {
146
+ findings.push({
147
+ severity: 'info',
148
+ category: 'publisher',
149
+ title: 'No npm provenance',
150
+ description: 'Package does not have npm provenance attestation (sigstore)',
151
+ });
152
+ }
153
+ if (maintainers.length > 10) {
154
+ findings.push({
155
+ severity: 'low',
156
+ category: 'publisher',
157
+ title: `Many maintainers: ${maintainers.length}`,
158
+ description: 'Large number of maintainers increases attack surface',
159
+ });
160
+ }
161
+ // Compute publisher score
162
+ const score = computePublisherScore(info);
163
+ return { info, findings, score };
164
+ }
165
+ catch (err) {
166
+ // Network failure — return neutral score
167
+ findings.push({
168
+ severity: 'info',
169
+ category: 'publisher',
170
+ title: 'Could not check npm registry',
171
+ description: err.message,
172
+ });
173
+ return { info: emptyInfo(packageName), findings, score: 50 };
174
+ }
175
+ }
176
+ // ─── Scoring ──────────────────────────────────────────────────────────
177
+ function computePublisherScore(info) {
178
+ let score = 0;
179
+ // Exists on npm: +20
180
+ if (info.existsOnNpm)
181
+ score += 20;
182
+ // Age bonus: up to +20
183
+ if (info.ageDays > 365)
184
+ score += 20;
185
+ else if (info.ageDays > 180)
186
+ score += 15;
187
+ else if (info.ageDays > 30)
188
+ score += 10;
189
+ else
190
+ score += 2;
191
+ // Downloads: up to +20
192
+ if (info.weeklyDownloads > 10_000)
193
+ score += 20;
194
+ else if (info.weeklyDownloads > 1_000)
195
+ score += 15;
196
+ else if (info.weeklyDownloads > 100)
197
+ score += 10;
198
+ else
199
+ score += 2;
200
+ // Repository: +10
201
+ if (info.hasRepository)
202
+ score += 10;
203
+ // License: +5
204
+ if (info.license)
205
+ score += 5;
206
+ // Provenance: +10
207
+ if (info.hasProvenance)
208
+ score += 10;
209
+ // Multiple maintainers but not too many: +5
210
+ if (info.maintainerCount >= 2 && info.maintainerCount <= 10)
211
+ score += 5;
212
+ else if (info.maintainerCount === 1)
213
+ score += 2;
214
+ // Multiple versions (established): +10
215
+ if (info.versionCount > 10)
216
+ score += 10;
217
+ else if (info.versionCount > 3)
218
+ score += 7;
219
+ else if (info.versionCount > 1)
220
+ score += 4;
221
+ else
222
+ score += 1;
223
+ return Math.min(100, score);
224
+ }
225
+ function emptyInfo(packageName) {
226
+ return {
227
+ packageName,
228
+ existsOnNpm: false,
229
+ maintainerCount: 0,
230
+ maintainers: [],
231
+ ageDays: 0,
232
+ weeklyDownloads: 0,
233
+ hasRepository: false,
234
+ hasProvenance: false,
235
+ versionCount: 0,
236
+ };
237
+ }
238
+ //# sourceMappingURL=publisher-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publisher-scanner.js","sourceRoot":"","sources":["../src/publisher-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Core scanner — orchestrates all sub-scanners and produces a unified ScanReport.
3
+ */
4
+ import { type DependencyScanResult } from './dependency-scanner.js';
5
+ import { type PatternScanResult } from './pattern-scanner.js';
6
+ import { type PermissionScanResult } from './permission-scanner.js';
7
+ import { type PublisherScanResult } from './publisher-scanner.js';
8
+ import { type ScoreBreakdown } from './trust-score.js';
9
+ export type FindingSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
10
+ export type FindingCategory = 'vulnerability' | 'dangerous-pattern' | 'obfuscation' | 'permission' | 'exfiltration' | 'publisher';
11
+ export interface Finding {
12
+ severity: FindingSeverity;
13
+ category: FindingCategory;
14
+ title: string;
15
+ description: string;
16
+ file?: string;
17
+ line?: number;
18
+ evidence?: string;
19
+ }
20
+ export interface TrustScore {
21
+ /** Overall score 0-100 (100 = fully trusted) */
22
+ overall: number;
23
+ /** Per-category breakdown */
24
+ breakdown: ScoreBreakdown;
25
+ /** Human-readable grade: A, B, C, D, F */
26
+ grade: string;
27
+ }
28
+ export interface ScanReport {
29
+ /** Package name */
30
+ packageName: string;
31
+ /** Package version */
32
+ packageVersion: string;
33
+ /** When scan was performed */
34
+ scannedAt: string;
35
+ /** Scanner version */
36
+ scannerVersion: string;
37
+ /** Trust score */
38
+ trustScore: TrustScore;
39
+ /** All findings */
40
+ findings: Finding[];
41
+ /** Dependency scan results */
42
+ dependencies: DependencyScanResult;
43
+ /** Code pattern scan results */
44
+ patterns: PatternScanResult;
45
+ /** Permission scan results */
46
+ permissions: PermissionScanResult;
47
+ /** Publisher identity results */
48
+ publisher?: PublisherScanResult;
49
+ /** Scan duration in ms */
50
+ durationMs: number;
51
+ }
52
+ export interface ScanOptions {
53
+ /** Path to the package directory to scan */
54
+ packagePath: string;
55
+ /** Skip dependency scanning (useful when npm not available) */
56
+ skipDependencies?: boolean;
57
+ /** Additional file extensions to scan (default: .ts, .js, .mjs, .cjs) */
58
+ extensions?: string[];
59
+ }
60
+ export declare function scan(options: ScanOptions): Promise<ScanReport>;
61
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAM1E,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAE9E,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,mBAAmB,GACnB,aAAa,GACb,YAAY,GACZ,cAAc,GACd,WAAW,CAAC;AAEhB,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,SAAS,EAAE,cAAc,CAAC;IAC1B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,mBAAmB;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,8BAA8B;IAC9B,YAAY,EAAE,oBAAoB,CAAC;IACnC,gCAAgC;IAChC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,8BAA8B;IAC9B,WAAW,EAAE,oBAAoB,CAAC;IAClC,iCAAiC;IACjC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAMD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAkEpE"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Core scanner — orchestrates all sub-scanners and produces a unified ScanReport.
3
+ */
4
+ import { scanDependencies } from './dependency-scanner.js';
5
+ import { scanCodePatterns } from './pattern-scanner.js';
6
+ import { scanPermissions } from './permission-scanner.js';
7
+ import { scanPublisher } from './publisher-scanner.js';
8
+ import { computeTrustScore } from './trust-score.js';
9
+ import { readFile } from 'node:fs/promises';
10
+ import { join } from 'node:path';
11
+ // ─── Main Scan Function ──────────────────────────────────────────────
12
+ const SCANNER_VERSION = '0.3.0';
13
+ export async function scan(options) {
14
+ const startTime = Date.now();
15
+ const { packagePath, skipDependencies = false } = options;
16
+ const extensions = options.extensions ?? ['.ts', '.js', '.mjs', '.cjs'];
17
+ // Read package.json
18
+ const pkgJsonPath = join(packagePath, 'package.json');
19
+ let packageName = 'unknown';
20
+ let packageVersion = '0.0.0';
21
+ try {
22
+ const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
23
+ packageName = pkgJson.name ?? 'unknown';
24
+ packageVersion = pkgJson.version ?? '0.0.0';
25
+ }
26
+ catch {
27
+ // No package.json — we can still scan the code
28
+ }
29
+ // Run all sub-scanners
30
+ const [dependencies, patterns, permissions, publisher] = await Promise.all([
31
+ skipDependencies
32
+ ? { vulnerabilities: [], totalDependencies: 0, findings: [] }
33
+ : scanDependencies(packagePath),
34
+ scanCodePatterns(packagePath, extensions),
35
+ scanPermissions(packagePath, extensions),
36
+ scanPublisher(packageName),
37
+ ]);
38
+ // Aggregate findings
39
+ const findings = [
40
+ ...dependencies.findings,
41
+ ...patterns.findings,
42
+ ...permissions.findings,
43
+ ...publisher.findings,
44
+ ];
45
+ // Compute trust score
46
+ const breakdown = computeTrustScore(findings, dependencies, patterns, permissions, publisher);
47
+ const overall = Math.round(breakdown.dependencies * 0.25 +
48
+ breakdown.codePatterns * 0.30 +
49
+ breakdown.permissions * 0.25 +
50
+ breakdown.publisher * 0.20);
51
+ const grade = overall >= 90 ? 'A'
52
+ : overall >= 75 ? 'B'
53
+ : overall >= 60 ? 'C'
54
+ : overall >= 40 ? 'D'
55
+ : 'F';
56
+ const trustScore = { overall, breakdown, grade };
57
+ return {
58
+ packageName,
59
+ packageVersion,
60
+ scannedAt: new Date().toISOString(),
61
+ scannerVersion: SCANNER_VERSION,
62
+ trustScore,
63
+ findings,
64
+ dependencies,
65
+ patterns,
66
+ permissions,
67
+ publisher,
68
+ durationMs: Date.now() - startTime,
69
+ };
70
+ }
71
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}