@aspect-guard/core 0.4.0 → 0.5.1

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/dist/index.d.ts CHANGED
@@ -160,15 +160,57 @@ declare class ExtensionGuardScanner {
160
160
  private hashDatabase;
161
161
  constructor(options?: Partial<ScanOptions>);
162
162
  scan(options?: Partial<ScanOptions>): Promise<FullScanReport>;
163
+ /**
164
+ * Scan multiple extensions concurrently with a configurable pool size.
165
+ * Uses a simple semaphore pattern to limit concurrent operations.
166
+ */
167
+ private scanExtensionsConcurrently;
163
168
  private scanExtension;
164
169
  private calculateTrustScore;
165
170
  private calculateRiskLevel;
166
171
  private calculateSummary;
167
172
  }
168
173
 
174
+ /**
175
+ * IDE extension paths organized by IDE name.
176
+ * Each IDE has multiple possible paths to support:
177
+ * - Different OS (Windows, macOS, Linux)
178
+ * - Different installation methods (user, system, portable)
179
+ * - Remote development scenarios (SSH, WSL, containers)
180
+ *
181
+ * Paths use these placeholders:
182
+ * - ~ : User home directory
183
+ * - %USERPROFILE% : Windows user profile
184
+ * - %APPDATA% : Windows AppData/Roaming
185
+ * - %LOCALAPPDATA% : Windows AppData/Local
186
+ *
187
+ * References:
188
+ * - https://code.visualstudio.com/docs/editor/extension-marketplace
189
+ * - https://code.visualstudio.com/docs/remote/troubleshooting
190
+ * - https://vscodium.com/
191
+ * - https://zed.dev/docs/extensions/installing-extensions
192
+ */
169
193
  declare const IDE_PATHS: Record<string, string[]>;
194
+ /**
195
+ * Expand path placeholders to actual paths
196
+ */
170
197
  declare function expandPath(inputPath: string): string;
198
+ /**
199
+ * Detect all installed IDE extension paths
200
+ */
171
201
  declare function detectIDEPaths(): DetectedIDE[];
202
+ /**
203
+ * Get list of all supported IDE names
204
+ */
205
+ declare function getSupportedIDEs(): string[];
206
+ /**
207
+ * Check if a specific IDE is installed
208
+ */
209
+ declare function isIDEInstalled(ideName: string): boolean;
210
+ /**
211
+ * Get the extension path for a specific IDE
212
+ */
213
+ declare function getIDEExtensionPath(ideName: string): string | null;
172
214
 
173
215
  declare function readExtension(extensionPath: string): Promise<ExtensionInfo | null>;
174
216
  declare function readExtensionsFromDirectory(directoryPath: string): Promise<ExtensionInfo[]>;
@@ -710,6 +752,6 @@ declare function generateBaseline(_extensionPaths: string[], _outputPath?: strin
710
752
  errors: string[];
711
753
  }>;
712
754
 
713
- declare const VERSION = "0.4.0";
755
+ declare const VERSION = "0.5.1";
714
756
 
715
- export { ALL_POPULAR_EXTENSIONS, type AdjustFindingsOptions, type AuditReport, type BundleDetectionResult, DETECTION_RULES, type DetectedIDE, type DetectionRule, type Evidence, type ExtensionCategory, ExtensionGuardScanner, type ExtensionHash, type ExtensionInfo, type ExtensionManifest, type Finding, type FindingCategory, type FullScanReport, type HashDatabase, IDE_PATHS, type InspectOptions, type IntegrityInfo, type IntegrityResult, type IntegrityStatus, JsonReporter, MEGA_POPULAR_EXTENSIONS, MarkdownReporter, POPULAR_EXTENSIONS, type PolicyAction, type PolicyConfig, PolicyEngine, type PolicyRules, type PolicyViolation, type PopularExtension, type Reporter, type ReporterOptions, type RiskLevel, RuleEngine, type RuleEngineOptions, SEVERITY_ORDER, SarifReporter, type ScanOptions, type ScanResult, type ScanSummary, type Severity, TRUSTED_EXTENSION_IDS, TRUSTED_PUBLISHERS, VERIFIED_PUBLISHERS, VERSION, addHash, adjustFindings, categorizeExtension, clearHashCache, collectFiles, compareSeverity, computeExtensionHashes, createHashRecord, detectBundle, detectIDEPaths, expandPath, generateBaseline, getDefaultDatabasePath, getHash, getPopularityTier, isAtLeastSeverity, isBundleOutputPath, isMegaPopular, isPopular, isTrustedExtension, isTrustedPublisher, isVerifiedPublisher, loadHashDatabase, loadPolicyConfig, readExtension, readExtensionsFromDirectory, registerBuiltInRules, ruleRegistry, saveHashDatabase, sha256, shouldCollectFile, shouldReduceSeverityForBundle, verifyIntegrity };
757
+ export { ALL_POPULAR_EXTENSIONS, type AdjustFindingsOptions, type AuditReport, type BundleDetectionResult, DETECTION_RULES, type DetectedIDE, type DetectionRule, type Evidence, type ExtensionCategory, ExtensionGuardScanner, type ExtensionHash, type ExtensionInfo, type ExtensionManifest, type Finding, type FindingCategory, type FullScanReport, type HashDatabase, IDE_PATHS, type InspectOptions, type IntegrityInfo, type IntegrityResult, type IntegrityStatus, JsonReporter, MEGA_POPULAR_EXTENSIONS, MarkdownReporter, POPULAR_EXTENSIONS, type PolicyAction, type PolicyConfig, PolicyEngine, type PolicyRules, type PolicyViolation, type PopularExtension, type Reporter, type ReporterOptions, type RiskLevel, RuleEngine, type RuleEngineOptions, SEVERITY_ORDER, SarifReporter, type ScanOptions, type ScanResult, type ScanSummary, type Severity, TRUSTED_EXTENSION_IDS, TRUSTED_PUBLISHERS, VERIFIED_PUBLISHERS, VERSION, addHash, adjustFindings, categorizeExtension, clearHashCache, collectFiles, compareSeverity, computeExtensionHashes, createHashRecord, detectBundle, detectIDEPaths, expandPath, generateBaseline, getDefaultDatabasePath, getHash, getIDEExtensionPath, getPopularityTier, getSupportedIDEs, isAtLeastSeverity, isBundleOutputPath, isIDEInstalled, isMegaPopular, isPopular, isTrustedExtension, isTrustedPublisher, isVerifiedPublisher, loadHashDatabase, loadPolicyConfig, readExtension, readExtensionsFromDirectory, registerBuiltInRules, ruleRegistry, saveHashDatabase, sha256, shouldCollectFile, shouldReduceSeverityForBundle, verifyIntegrity };
package/dist/index.js CHANGED
@@ -22,27 +22,151 @@ import * as fs from "fs";
22
22
  import * as os from "os";
23
23
  import * as path from "path";
24
24
  var IDE_PATHS = {
25
- "VS Code": ["~/.vscode/extensions"],
26
- "VS Code Insiders": ["~/.vscode-insiders/extensions"],
25
+ // VS Code - Standard installation
26
+ "VS Code": [
27
+ // Linux & macOS
28
+ "~/.vscode/extensions",
29
+ // Windows
30
+ "%USERPROFILE%/.vscode/extensions",
31
+ "%USERPROFILE%\\.vscode\\extensions"
32
+ ],
33
+ // VS Code Insiders - Preview builds
34
+ "VS Code Insiders": [
35
+ // Linux & macOS
36
+ "~/.vscode-insiders/extensions",
37
+ // Windows
38
+ "%USERPROFILE%/.vscode-insiders/extensions",
39
+ "%USERPROFILE%\\.vscode-insiders\\extensions"
40
+ ],
41
+ // VS Code Server - Remote SSH connections
27
42
  "VS Code Server": ["~/.vscode-server/extensions"],
28
- Cursor: ["~/.cursor/extensions"],
29
- Windsurf: ["~/.windsurf/extensions"],
30
- Trae: ["~/.trae/extensions"],
31
- VSCodium: ["~/.vscode-oss/extensions"]
43
+ // VS Code Server Insiders - Remote SSH for Insiders
44
+ "VS Code Server Insiders": ["~/.vscode-server-insiders/extensions"],
45
+ // Cursor - AI-powered code editor (VS Code fork)
46
+ // https://cursor.com
47
+ Cursor: [
48
+ // Linux & macOS
49
+ "~/.cursor/extensions",
50
+ // Windows
51
+ "%USERPROFILE%/.cursor/extensions",
52
+ "%USERPROFILE%\\.cursor\\extensions",
53
+ // macOS alternate location
54
+ "~/Library/Application Support/Cursor/User/extensions"
55
+ ],
56
+ // Cursor Server - Remote Cursor connections
57
+ "Cursor Server": ["~/.cursor-server/extensions"],
58
+ // Windsurf - Codeium AI IDE (VS Code fork)
59
+ // https://codeium.com/windsurf
60
+ Windsurf: [
61
+ // Linux & macOS
62
+ "~/.windsurf/extensions",
63
+ // Windows
64
+ "%USERPROFILE%/.windsurf/extensions",
65
+ "%USERPROFILE%\\.windsurf\\extensions",
66
+ // macOS alternate location
67
+ "~/Library/Application Support/Windsurf/extensions"
68
+ ],
69
+ // Windsurf Server - Remote Windsurf connections
70
+ "Windsurf Server": ["~/.windsurf-server/extensions"],
71
+ // Trae - ByteDance AI IDE (VS Code fork)
72
+ // https://www.trae.ai / https://www.marscode.com
73
+ Trae: [
74
+ // Linux & macOS
75
+ "~/.trae/extensions",
76
+ // Windows
77
+ "%USERPROFILE%/.trae/extensions",
78
+ "%USERPROFILE%\\.trae\\extensions"
79
+ ],
80
+ // VSCodium - Open source VS Code without telemetry
81
+ // https://vscodium.com
82
+ VSCodium: [
83
+ // Linux & macOS
84
+ "~/.vscode-oss/extensions",
85
+ // Windows
86
+ "%USERPROFILE%/.vscode-oss/extensions",
87
+ "%USERPROFILE%\\.vscode-oss\\extensions",
88
+ // Flatpak installation (Linux)
89
+ "~/.var/app/com.vscodium.codium/data/codium/extensions"
90
+ ],
91
+ // VSCodium Insiders
92
+ "VSCodium Insiders": [
93
+ "~/.vscode-oss-insiders/extensions",
94
+ "%USERPROFILE%/.vscode-oss-insiders/extensions"
95
+ ],
96
+ // Code - OSS (open source build from Microsoft repo)
97
+ "Code - OSS": ["~/.config/Code - OSS/extensions", "~/.vscode-oss/extensions"],
98
+ // Positron - Posit's data science IDE (VS Code fork)
99
+ // https://github.com/posit-dev/positron
100
+ Positron: [
101
+ "~/.positron/extensions",
102
+ "%USERPROFILE%/.positron/extensions",
103
+ "~/Library/Application Support/Positron/extensions"
104
+ ],
105
+ // Theia - Eclipse Theia IDE (VS Code compatible)
106
+ // https://theia-ide.org
107
+ Theia: ["~/.theia/extensions", "%USERPROFILE%/.theia/extensions"],
108
+ // OpenVSCode Server - Web-based VS Code
109
+ // https://github.com/gitpod-io/openvscode-server
110
+ "OpenVSCode Server": ["~/.openvscode-server/extensions"],
111
+ // code-server - VS Code in the browser
112
+ // https://github.com/coder/code-server
113
+ "code-server": ["~/.local/share/code-server/extensions", "~/.config/code-server/extensions"],
114
+ // GitHub Codespaces (when accessed locally)
115
+ "GitHub Codespaces": ["~/.codespaces/.vscode-remote/extensions"],
116
+ // Gitpod
117
+ Gitpod: ["/workspace/.gitpod/extensions", "~/.gitpod/extensions"],
118
+ // DevPod
119
+ DevPod: ["~/.devpod/extensions"]
120
+ // Lapce - Lightning-fast native code editor (has its own extension format but partially compatible)
121
+ // https://lapce.dev
122
+ // Note: Lapce uses a different extension format, included for future compatibility
123
+ // Lapce: [
124
+ // '~/.lapce/plugins',
125
+ // '~/Library/Application Support/Lapce/plugins',
126
+ // ],
127
+ // Zed - High-performance editor (different extension format, not VS Code compatible)
128
+ // https://zed.dev
129
+ // Note: Zed uses its own extension format, not VS Code compatible
130
+ // Included as reference for potential future support
131
+ // Zed: [
132
+ // '~/Library/Application Support/Zed/extensions',
133
+ // '~/.local/share/zed/extensions',
134
+ // ],
32
135
  };
33
136
  function expandPath(inputPath) {
34
- if (inputPath.startsWith("~")) {
35
- return path.join(os.homedir(), inputPath.slice(1));
36
- }
37
- if (inputPath.includes("%USERPROFILE%")) {
38
- return inputPath.replace("%USERPROFILE%", os.homedir());
137
+ let result = inputPath;
138
+ const home = os.homedir();
139
+ if (result.startsWith("~/")) {
140
+ result = path.join(home, result.slice(2));
141
+ } else if (result.startsWith("~\\")) {
142
+ result = path.join(home, result.slice(2));
143
+ } else if (result === "~") {
144
+ result = home;
145
+ }
146
+ if (process.platform === "win32") {
147
+ result = result.replace(/%USERPROFILE%/gi, home);
148
+ result = result.replace(
149
+ /%APPDATA%/gi,
150
+ process.env.APPDATA || path.join(home, "AppData", "Roaming")
151
+ );
152
+ result = result.replace(
153
+ /%LOCALAPPDATA%/gi,
154
+ process.env.LOCALAPPDATA || path.join(home, "AppData", "Local")
155
+ );
156
+ } else {
157
+ result = result.replace(/%USERPROFILE%/gi, home);
39
158
  }
40
- return inputPath;
159
+ return path.normalize(result);
41
160
  }
42
161
  function countExtensions(dirPath) {
43
162
  try {
44
163
  const entries = fs.readdirSync(dirPath, { withFileTypes: true });
45
- return entries.filter((entry) => entry.isDirectory()).length;
164
+ return entries.filter((entry) => {
165
+ if (!entry.isDirectory()) return false;
166
+ if (entry.name.startsWith(".")) return false;
167
+ if (entry.name === "node_modules") return false;
168
+ return true;
169
+ }).length;
46
170
  } catch {
47
171
  return 0;
48
172
  }
@@ -53,17 +177,42 @@ function detectIDEPaths() {
53
177
  for (const idePath of paths) {
54
178
  const expandedPath = expandPath(idePath);
55
179
  if (fs.existsSync(expandedPath)) {
56
- detected.push({
57
- name: ideName,
58
- path: expandedPath,
59
- extensionCount: countExtensions(expandedPath)
60
- });
61
- break;
180
+ const extensionCount = countExtensions(expandedPath);
181
+ if (extensionCount > 0) {
182
+ detected.push({
183
+ name: ideName,
184
+ path: expandedPath,
185
+ extensionCount
186
+ });
187
+ break;
188
+ }
62
189
  }
63
190
  }
64
191
  }
65
192
  return detected;
66
193
  }
194
+ function getSupportedIDEs() {
195
+ return Object.keys(IDE_PATHS);
196
+ }
197
+ function isIDEInstalled(ideName) {
198
+ const paths = IDE_PATHS[ideName];
199
+ if (!paths) return false;
200
+ return paths.some((p) => {
201
+ const expanded = expandPath(p);
202
+ return fs.existsSync(expanded) && countExtensions(expanded) > 0;
203
+ });
204
+ }
205
+ function getIDEExtensionPath(ideName) {
206
+ const paths = IDE_PATHS[ideName];
207
+ if (!paths) return null;
208
+ for (const p of paths) {
209
+ const expanded = expandPath(p);
210
+ if (fs.existsSync(expanded)) {
211
+ return expanded;
212
+ }
213
+ }
214
+ return null;
215
+ }
67
216
 
68
217
  // src/scanner/extension-reader.ts
69
218
  import * as fs2 from "fs/promises";
@@ -641,7 +790,11 @@ var EXPECTED_BEHAVIORS = {
641
790
  {
642
791
  // SCM extensions may spawn git processes
643
792
  ruleIds: ["EG-CRIT-002"],
644
- matchedPatterns: ["child_process-exec", "child_process-execSync", "child_process-spawn-shell"]
793
+ matchedPatterns: [
794
+ "child_process-exec",
795
+ "child_process-execSync",
796
+ "child_process-spawn-shell"
797
+ ]
645
798
  }
646
799
  ],
647
800
  debugger: [
@@ -1996,9 +2149,7 @@ var ExtensionGuardScanner = class {
1996
2149
  minSeverity: this.options.severity
1997
2150
  });
1998
2151
  if (this.options.verifyIntegrity) {
1999
- this.hashDatabase = loadHashDatabase(
2000
- this.options.hashDatabasePath || void 0
2001
- );
2152
+ this.hashDatabase = loadHashDatabase(this.options.hashDatabasePath || void 0);
2002
2153
  }
2003
2154
  }
2004
2155
  async scan(options) {
@@ -2027,11 +2178,8 @@ var ExtensionGuardScanner = class {
2027
2178
  }
2028
2179
  ide.extensionCount = allExtensions[i].length;
2029
2180
  }
2030
- const results = [];
2031
- for (const { ext } of extensionMap.values()) {
2032
- const result = await this.scanExtension(ext);
2033
- results.push(result);
2034
- }
2181
+ const extensions = Array.from(extensionMap.values()).map(({ ext }) => ext);
2182
+ const results = await this.scanExtensionsConcurrently(extensions, mergedOptions.concurrency);
2035
2183
  const summary = this.calculateSummary(results);
2036
2184
  return {
2037
2185
  scanId: randomUUID2(),
@@ -2048,6 +2196,29 @@ var ExtensionGuardScanner = class {
2048
2196
  scanDurationMs: Date.now() - startTime
2049
2197
  };
2050
2198
  }
2199
+ /**
2200
+ * Scan multiple extensions concurrently with a configurable pool size.
2201
+ * Uses a simple semaphore pattern to limit concurrent operations.
2202
+ */
2203
+ async scanExtensionsConcurrently(extensions, concurrency) {
2204
+ const results = new Array(extensions.length);
2205
+ let currentIndex = 0;
2206
+ const worker = async () => {
2207
+ while (currentIndex < extensions.length) {
2208
+ const index = currentIndex++;
2209
+ const ext = extensions[index];
2210
+ if (ext) {
2211
+ results[index] = await this.scanExtension(ext);
2212
+ }
2213
+ }
2214
+ };
2215
+ const workers = Array.from(
2216
+ { length: Math.min(concurrency, extensions.length) },
2217
+ () => worker()
2218
+ );
2219
+ await Promise.all(workers);
2220
+ return results.filter((r) => r !== void 0);
2221
+ }
2051
2222
  async scanExtension(ext) {
2052
2223
  const startTime = Date.now();
2053
2224
  const files = await collectFiles(ext.installPath);
@@ -2124,9 +2295,7 @@ var ExtensionGuardScanner = class {
2124
2295
  return Math.max(0, Math.min(100, score));
2125
2296
  }
2126
2297
  calculateRiskLevel(trustScore, findings) {
2127
- const realFindings = findings.filter(
2128
- (f) => !f.description?.includes("[Downgraded:")
2129
- );
2298
+ const realFindings = findings.filter((f) => !f.description?.includes("[Downgraded:"));
2130
2299
  if (realFindings.some((f) => f.severity === "critical")) {
2131
2300
  return "critical";
2132
2301
  }
@@ -2847,7 +3016,7 @@ var PolicyEngine = class {
2847
3016
  };
2848
3017
 
2849
3018
  // src/index.ts
2850
- var VERSION = "0.4.0";
3019
+ var VERSION = "0.5.1";
2851
3020
  export {
2852
3021
  ALL_POPULAR_EXTENSIONS,
2853
3022
  DETECTION_RULES,
@@ -2879,9 +3048,12 @@ export {
2879
3048
  generateBaseline,
2880
3049
  getDefaultDatabasePath,
2881
3050
  getHash,
3051
+ getIDEExtensionPath,
2882
3052
  getPopularityTier,
3053
+ getSupportedIDEs,
2883
3054
  isAtLeastSeverity,
2884
3055
  isBundleOutputPath,
3056
+ isIDEInstalled,
2885
3057
  isMegaPopular,
2886
3058
  isPopular,
2887
3059
  isTrustedExtension,