vantaverse-ai-reviewer 0.3.1 → 0.3.3
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/ai/agent.d.ts.map +1 -1
- package/dist/ai/agent.js +2 -1
- package/dist/ai/agent.js.map +1 -1
- package/dist/ai/gemini-client.d.ts.map +1 -1
- package/dist/ai/gemini-client.js +6 -2
- package/dist/ai/gemini-client.js.map +1 -1
- package/dist/ai/prompts.d.ts +1 -0
- package/dist/ai/prompts.d.ts.map +1 -1
- package/dist/ai/prompts.js +31 -1
- package/dist/ai/prompts.js.map +1 -1
- package/dist/auth/token-manager.d.ts +1 -1
- package/dist/auth/token-manager.d.ts.map +1 -1
- package/dist/auth/token-manager.js +22 -2
- package/dist/auth/token-manager.js.map +1 -1
- package/dist/commands/scan.d.ts +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +228 -126
- package/dist/commands/scan.js.map +1 -1
- package/dist/core/executor.d.ts +1 -0
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/executor.js +16 -6
- package/dist/core/executor.js.map +1 -1
- package/dist/core/security-scanner.d.ts.map +1 -1
- package/dist/core/security-scanner.js +22 -15
- package/dist/core/security-scanner.js.map +1 -1
- package/dist/core/security.d.ts +18 -7
- package/dist/core/security.d.ts.map +1 -1
- package/dist/core/security.js +120 -48
- package/dist/core/security.js.map +1 -1
- package/dist/utils/folder-tree.d.ts +22 -0
- package/dist/utils/folder-tree.d.ts.map +1 -0
- package/dist/utils/folder-tree.js +103 -0
- package/dist/utils/folder-tree.js.map +1 -0
- package/dist/utils/package-analyzer.d.ts +27 -0
- package/dist/utils/package-analyzer.d.ts.map +1 -0
- package/dist/utils/package-analyzer.js +102 -0
- package/dist/utils/package-analyzer.js.map +1 -0
- package/dist/utils/token-estimator.d.ts.map +1 -1
- package/dist/utils/token-estimator.js +4 -2
- package/dist/utils/token-estimator.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,28 +5,35 @@ import { executeCommand } from './executor.js';
|
|
|
5
5
|
import { safeReadFile } from './security.js';
|
|
6
6
|
/**
|
|
7
7
|
* Secret detection patterns (regex-based, zero cost)
|
|
8
|
+
* Patterns are designed to be ReDoS-safe with:
|
|
9
|
+
* - Bounded quantifiers (no nested *)
|
|
10
|
+
* - Specific character counts where possible
|
|
11
|
+
* - Non-backtracking character classes
|
|
8
12
|
*/
|
|
9
13
|
const SECRET_PATTERNS = [
|
|
10
|
-
// AWS
|
|
14
|
+
// AWS - specific format with exact lengths
|
|
11
15
|
{ pattern: /AKIA[0-9A-Z]{16}/g, type: 'AWS Access Key', severity: 'critical' },
|
|
12
|
-
{ pattern: /
|
|
13
|
-
// Generic API Keys
|
|
14
|
-
{ pattern: /
|
|
15
|
-
{ pattern: /
|
|
16
|
-
// JWT Tokens
|
|
17
|
-
{ pattern: /eyJ[A-Za-z0-
|
|
18
|
-
// GitHub
|
|
16
|
+
{ pattern: /aws_secret_access_key\s*=\s*[A-Za-z0-9/+=]{40}/gi, type: 'AWS Secret Key', severity: 'critical' },
|
|
17
|
+
// Generic API Keys - bounded lengths, non-greedy
|
|
18
|
+
{ pattern: /api_key\s*=\s*["'][A-Za-z0-9_-]{20,64}["']/gi, type: 'API Key', severity: 'high' },
|
|
19
|
+
{ pattern: /secret\s*=\s*["'][A-Za-z0-9_-]{20,64}["']/gi, type: 'Secret/Token', severity: 'high' },
|
|
20
|
+
// JWT Tokens - use possessive-like matching with specific structure
|
|
21
|
+
{ pattern: /eyJ[A-Za-z0-9_-]{10,500}\.eyJ[A-Za-z0-9_-]{10,500}\.[A-Za-z0-9_.-]{10,500}/g, type: 'JWT Token', severity: 'medium' },
|
|
22
|
+
// GitHub - exact formats
|
|
19
23
|
{ pattern: /ghp_[A-Za-z0-9]{36}/g, type: 'GitHub Personal Token', severity: 'critical' },
|
|
20
24
|
{ pattern: /github_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}/g, type: 'GitHub PAT', severity: 'critical' },
|
|
21
|
-
|
|
25
|
+
{ pattern: /ghs_[A-Za-z0-9]{36}/g, type: 'GitHub App Token', severity: 'critical' },
|
|
26
|
+
// Private Keys - simple literal match
|
|
22
27
|
{ pattern: /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/g, type: 'Private Key', severity: 'critical' },
|
|
23
|
-
// Database URLs
|
|
24
|
-
{ pattern: /(?:mongodb|postgres|mysql|redis):\/\/[
|
|
25
|
-
// Slack
|
|
28
|
+
// Database URLs - bounded non-whitespace with max length
|
|
29
|
+
{ pattern: /(?:mongodb|postgres|mysql|redis):\/\/[^\s"']{10,200}/gi, type: 'Database URL with Credentials', severity: 'critical' },
|
|
30
|
+
// Slack - exact format with specific lengths
|
|
26
31
|
{ pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}/g, type: 'Slack Token', severity: 'high' },
|
|
27
|
-
// Stripe
|
|
28
|
-
{ pattern: /sk_live_[A-Za-z0-9]{24,}/g, type: 'Stripe Secret Key', severity: 'critical' },
|
|
29
|
-
{ pattern: /pk_live_[A-Za-z0-9]{24,}/g, type: 'Stripe Publishable Key', severity: 'medium' },
|
|
32
|
+
// Stripe - exact prefix with bounded length
|
|
33
|
+
{ pattern: /sk_live_[A-Za-z0-9]{24,50}/g, type: 'Stripe Secret Key', severity: 'critical' },
|
|
34
|
+
{ pattern: /pk_live_[A-Za-z0-9]{24,50}/g, type: 'Stripe Publishable Key', severity: 'medium' },
|
|
35
|
+
// Google API Key
|
|
36
|
+
{ pattern: /AIza[A-Za-z0-9_-]{35}/g, type: 'Google API Key', severity: 'high' },
|
|
30
37
|
];
|
|
31
38
|
/**
|
|
32
39
|
* Scan files for secrets using regex patterns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-scanner.js","sourceRoot":"","sources":["../../src/core/security-scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAyB7C
|
|
1
|
+
{"version":3,"file":"security-scanner.js","sourceRoot":"","sources":["../../src/core/security-scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/core/security.d.ts
CHANGED
|
@@ -17,12 +17,9 @@ export declare function isWithinRepo(filePath: string, repoRoot: string): boolea
|
|
|
17
17
|
* Validate a path and throw if it's outside the repo
|
|
18
18
|
*/
|
|
19
19
|
export declare function validatePath(filePath: string, repoRoot: string): string;
|
|
20
|
-
/**
|
|
21
|
-
* Check if path is a symlink pointing outside repo
|
|
22
|
-
*/
|
|
23
|
-
export declare function isSymlinkEscape(filePath: string, repoRoot: string): Promise<boolean>;
|
|
24
20
|
/**
|
|
25
21
|
* Safe file read with security validation
|
|
22
|
+
* Resolves symlinks FIRST to prevent TOCTOU race conditions
|
|
26
23
|
*/
|
|
27
24
|
export declare function safeReadFile(filePath: string, repoRoot: string, maxSizeBytes?: number): Promise<string>;
|
|
28
25
|
/**
|
|
@@ -30,11 +27,25 @@ export declare function safeReadFile(filePath: string, repoRoot: string, maxSize
|
|
|
30
27
|
*/
|
|
31
28
|
export declare const SECURITY_IGNORE_PATTERNS: string[];
|
|
32
29
|
/**
|
|
33
|
-
* Validate that a command
|
|
30
|
+
* Validate that a command and its first argument are in the allowlist
|
|
34
31
|
*/
|
|
35
|
-
export declare function isAllowedCommand(command: string): boolean;
|
|
32
|
+
export declare function isAllowedCommand(command: string, args?: string[]): boolean;
|
|
36
33
|
/**
|
|
37
|
-
*
|
|
34
|
+
* Comprehensive argument sanitization to prevent shell injection
|
|
35
|
+
* Strips ALL potentially dangerous characters for both Unix and Windows shells
|
|
38
36
|
*/
|
|
39
37
|
export declare function sanitizeArgs(args: string[]): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Validate that an output path is safe to write to
|
|
40
|
+
* Must be within the working directory or a subdirectory
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateOutputPath(outputPath: string, cwd: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Validate git branch name to prevent command injection
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateBranchName(branchName: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Sanitize error messages to prevent credential leakage
|
|
49
|
+
*/
|
|
50
|
+
export declare function sanitizeErrorMessage(error: unknown): string;
|
|
40
51
|
//# sourceMappingURL=security.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/core/security.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,aAAc,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI9B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOxE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUvE;AAED
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/core/security.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,aAAc,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI9B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOxE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUvE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,GAAE,MAAoB,GACnC,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,UAUpC,CAAC;AAYF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAmB9E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAwBrD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAoB1E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAqB3D"}
|
package/dist/core/security.js
CHANGED
|
@@ -32,42 +32,34 @@ export function isWithinRepo(filePath, repoRoot) {
|
|
|
32
32
|
export function validatePath(filePath, repoRoot) {
|
|
33
33
|
const normalized = normalizePath(filePath);
|
|
34
34
|
if (!isWithinRepo(normalized, repoRoot)) {
|
|
35
|
-
throw new SecurityError(`Access denied:
|
|
35
|
+
throw new SecurityError(`Access denied: Path is outside the repository boundary`);
|
|
36
36
|
}
|
|
37
37
|
return normalized;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Check if path is a symlink pointing outside repo
|
|
41
|
-
*/
|
|
42
|
-
export async function isSymlinkEscape(filePath, repoRoot) {
|
|
43
|
-
try {
|
|
44
|
-
const stats = await fs.promises.lstat(filePath);
|
|
45
|
-
if (stats.isSymbolicLink()) {
|
|
46
|
-
const realPath = await fs.promises.realpath(filePath);
|
|
47
|
-
return !isWithinRepo(realPath, repoRoot);
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
39
|
/**
|
|
56
40
|
* Safe file read with security validation
|
|
41
|
+
* Resolves symlinks FIRST to prevent TOCTOU race conditions
|
|
57
42
|
*/
|
|
58
43
|
export async function safeReadFile(filePath, repoRoot, maxSizeBytes = 1024 * 1024 // 1MB default
|
|
59
44
|
) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
// First, resolve to real path (follows symlinks)
|
|
46
|
+
let realPath;
|
|
47
|
+
try {
|
|
48
|
+
realPath = await fs.promises.realpath(filePath);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new SecurityError(`Cannot resolve path: File does not exist or is inaccessible`);
|
|
52
|
+
}
|
|
53
|
+
// Validate the REAL path is within repo (prevents symlink escape)
|
|
54
|
+
if (!isWithinRepo(realPath, repoRoot)) {
|
|
55
|
+
throw new SecurityError(`Access denied: Path resolves outside the repository boundary`);
|
|
64
56
|
}
|
|
65
57
|
// Check file size
|
|
66
|
-
const stats = await fs.promises.stat(
|
|
58
|
+
const stats = await fs.promises.stat(realPath);
|
|
67
59
|
if (stats.size > maxSizeBytes) {
|
|
68
|
-
throw new SecurityError(`File too large:
|
|
60
|
+
throw new SecurityError(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB exceeds ${(maxSizeBytes / 1024 / 1024).toFixed(2)}MB limit`);
|
|
69
61
|
}
|
|
70
|
-
return fs.promises.readFile(
|
|
62
|
+
return fs.promises.readFile(realPath, 'utf-8');
|
|
71
63
|
}
|
|
72
64
|
/**
|
|
73
65
|
* Default patterns to always ignore (security sensitive)
|
|
@@ -84,35 +76,115 @@ export const SECURITY_IGNORE_PATTERNS = [
|
|
|
84
76
|
'**/.ssh/**',
|
|
85
77
|
];
|
|
86
78
|
/**
|
|
87
|
-
*
|
|
79
|
+
* Allowed commands with their permitted subcommands
|
|
80
|
+
* This provides defense-in-depth against command injection
|
|
81
|
+
*/
|
|
82
|
+
const ALLOWED_COMMANDS = new Map([
|
|
83
|
+
['git', new Set(['status', 'log', 'diff', 'branch', 'grep', 'show-current'])],
|
|
84
|
+
['npm', new Set(['outdated', 'list', 'audit'])],
|
|
85
|
+
['npx', new Set(['tsc', 'eslint'])], // Only specific trusted packages
|
|
86
|
+
]);
|
|
87
|
+
/**
|
|
88
|
+
* Validate that a command and its first argument are in the allowlist
|
|
88
89
|
*/
|
|
89
|
-
export function isAllowedCommand(command) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
];
|
|
106
|
-
const baseCommand = command.trim().split(/\s+/)[0];
|
|
107
|
-
return allowedCommands.includes(baseCommand);
|
|
90
|
+
export function isAllowedCommand(command, args = []) {
|
|
91
|
+
const baseCommand = command.trim().split(/\s+/)[0].toLowerCase();
|
|
92
|
+
// Check if command is in allowlist
|
|
93
|
+
const allowedSubcommands = ALLOWED_COMMANDS.get(baseCommand);
|
|
94
|
+
if (!allowedSubcommands) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// For commands with subcommands, validate the first arg
|
|
98
|
+
if (args.length > 0 && allowedSubcommands.size > 0) {
|
|
99
|
+
const firstArg = args[0].replace(/^-+/, ''); // Remove leading dashes
|
|
100
|
+
// Allow if it's in the subcommand list OR if it starts with - (flags)
|
|
101
|
+
if (!args[0].startsWith('-') && !allowedSubcommands.has(args[0])) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
108
106
|
}
|
|
109
107
|
/**
|
|
110
|
-
*
|
|
108
|
+
* Comprehensive argument sanitization to prevent shell injection
|
|
109
|
+
* Strips ALL potentially dangerous characters for both Unix and Windows shells
|
|
111
110
|
*/
|
|
112
111
|
export function sanitizeArgs(args) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
// Dangerous characters for shell injection
|
|
113
|
+
const DANGEROUS_CHARS = /[;&|`$(){}[\]<>"'%^@#\r\n\t\\!?*~]/g;
|
|
114
|
+
// Additional validation patterns
|
|
115
|
+
const OPTION_INJECTION = /^-/; // Arguments starting with - could be interpreted as options
|
|
116
|
+
return args.map((arg, index) => {
|
|
117
|
+
// Remove all dangerous characters
|
|
118
|
+
let sanitized = arg.replace(DANGEROUS_CHARS, '');
|
|
119
|
+
// Prevent null byte injection
|
|
120
|
+
sanitized = sanitized.replace(/\0/g, '');
|
|
121
|
+
// Trim whitespace
|
|
122
|
+
sanitized = sanitized.trim();
|
|
123
|
+
// Prevent empty arguments after sanitization
|
|
124
|
+
if (sanitized.length === 0 && arg.length > 0) {
|
|
125
|
+
throw new SecurityError(`Argument ${index} contains only unsafe characters`);
|
|
126
|
+
}
|
|
127
|
+
return sanitized;
|
|
116
128
|
});
|
|
117
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Validate that an output path is safe to write to
|
|
132
|
+
* Must be within the working directory or a subdirectory
|
|
133
|
+
*/
|
|
134
|
+
export function validateOutputPath(outputPath, cwd) {
|
|
135
|
+
const resolved = path.resolve(cwd, outputPath);
|
|
136
|
+
const normalizedCwd = normalizePath(cwd);
|
|
137
|
+
if (!resolved.startsWith(normalizedCwd + path.sep) && resolved !== normalizedCwd) {
|
|
138
|
+
throw new SecurityError(`Output path must be within the current working directory`);
|
|
139
|
+
}
|
|
140
|
+
// Prevent writing to sensitive files
|
|
141
|
+
const basename = path.basename(resolved).toLowerCase();
|
|
142
|
+
const sensitiveNames = ['.env', '.gitignore', 'package.json', 'package-lock.json', '.npmrc'];
|
|
143
|
+
if (sensitiveNames.includes(basename)) {
|
|
144
|
+
throw new SecurityError(`Cannot write to sensitive file: ${basename}`);
|
|
145
|
+
}
|
|
146
|
+
return resolved;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validate git branch name to prevent command injection
|
|
150
|
+
*/
|
|
151
|
+
export function validateBranchName(branchName) {
|
|
152
|
+
// Git branch naming rules: alphanumeric, dash, underscore, slash, dot
|
|
153
|
+
const SAFE_BRANCH_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.\-\/]*$/;
|
|
154
|
+
if (!SAFE_BRANCH_PATTERN.test(branchName)) {
|
|
155
|
+
throw new SecurityError(`Invalid branch name. Use only: letters, numbers, _, ., -, /`);
|
|
156
|
+
}
|
|
157
|
+
// Prevent option injection
|
|
158
|
+
if (branchName.startsWith('-')) {
|
|
159
|
+
throw new SecurityError(`Branch name cannot start with a hyphen`);
|
|
160
|
+
}
|
|
161
|
+
// Prevent path traversal
|
|
162
|
+
if (branchName.includes('..')) {
|
|
163
|
+
throw new SecurityError(`Branch name cannot contain '..'`);
|
|
164
|
+
}
|
|
165
|
+
// Max length check
|
|
166
|
+
if (branchName.length > 255) {
|
|
167
|
+
throw new SecurityError(`Branch name too long (max 255 characters)`);
|
|
168
|
+
}
|
|
169
|
+
return branchName;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Sanitize error messages to prevent credential leakage
|
|
173
|
+
*/
|
|
174
|
+
export function sanitizeErrorMessage(error) {
|
|
175
|
+
let message = 'Unknown error';
|
|
176
|
+
if (error instanceof Error) {
|
|
177
|
+
message = error.message;
|
|
178
|
+
// Remove potential API keys (30+ alphanumeric characters)
|
|
179
|
+
message = message.replace(/[A-Za-z0-9_-]{30,}/g, '[REDACTED]');
|
|
180
|
+
// Remove URL query parameters that might contain keys
|
|
181
|
+
message = message.replace(/[?&](key|token|api_key|apikey|secret)=[^&\s]*/gi, '?$1=[REDACTED]');
|
|
182
|
+
// Remove bearer tokens
|
|
183
|
+
message = message.replace(/Bearer\s+[A-Za-z0-9._-]+/gi, 'Bearer [REDACTED]');
|
|
184
|
+
// Remove file paths that might reveal system info
|
|
185
|
+
message = message.replace(/[A-Z]:\\[^\s:]+/gi, '[PATH]'); // Windows paths
|
|
186
|
+
message = message.replace(/\/home\/[^\s:]+/g, '[PATH]'); // Unix home paths
|
|
187
|
+
}
|
|
188
|
+
return message;
|
|
189
|
+
}
|
|
118
190
|
//# sourceMappingURL=security.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/core/security.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,OAAO,aAAc,SAAQ,KAAK;IACpC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAChC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,QAAgB;IAC3D,MAAM,cAAc,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAE/C,4CAA4C;IAC5C,OAAO,cAAc,CAAC,UAAU,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC;QACvD,cAAc,KAAK,cAAc,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,QAAgB;IAC3D,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,aAAa,CACnB,
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/core/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder Tree Generator - Creates visual folder structure
|
|
3
|
+
*/
|
|
4
|
+
export interface TreeOptions {
|
|
5
|
+
maxDepth?: number;
|
|
6
|
+
exclude?: string[];
|
|
7
|
+
includeFiles?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generate a text-based folder tree structure
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateFolderTree(rootDir: string, options?: TreeOptions): Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Get a summary of the folder structure
|
|
15
|
+
*/
|
|
16
|
+
export declare function getFolderSummary(rootDir: string): Promise<{
|
|
17
|
+
totalFiles: number;
|
|
18
|
+
totalDirs: number;
|
|
19
|
+
byExtension: Record<string, number>;
|
|
20
|
+
topLevelDirs: string[];
|
|
21
|
+
}>;
|
|
22
|
+
//# sourceMappingURL=folder-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-tree.d.ts","sourceRoot":"","sources":["../../src/utils/folder-tree.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAOD;;GAEG;AACH,wBAAsB,kBAAkB,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,WAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAcjB;AAwED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC,CA+BD"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder Tree Generator - Creates visual folder structure
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
const DEFAULT_EXCLUDE = [
|
|
7
|
+
'node_modules', '.git', '.next', 'dist', 'build',
|
|
8
|
+
'.cache', 'coverage', '.turbo', '.vercel'
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Generate a text-based folder tree structure
|
|
12
|
+
*/
|
|
13
|
+
export async function generateFolderTree(rootDir, options = {}) {
|
|
14
|
+
const { maxDepth = 5, exclude = DEFAULT_EXCLUDE, includeFiles = true } = options;
|
|
15
|
+
const lines = [];
|
|
16
|
+
const rootName = path.basename(rootDir);
|
|
17
|
+
lines.push(`📁 ${rootName}/`);
|
|
18
|
+
await walkDir(rootDir, '', 0, maxDepth, exclude, includeFiles, lines);
|
|
19
|
+
return lines.join('\n');
|
|
20
|
+
}
|
|
21
|
+
async function walkDir(dir, prefix, depth, maxDepth, exclude, includeFiles, lines) {
|
|
22
|
+
if (depth >= maxDepth) {
|
|
23
|
+
lines.push(`${prefix}└── ...`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let entries;
|
|
27
|
+
try {
|
|
28
|
+
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Filter and sort: directories first, then files
|
|
34
|
+
const filtered = entries.filter(e => !exclude.includes(e.name) && !e.name.startsWith('.'));
|
|
35
|
+
const dirs = filtered.filter(e => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
|
|
36
|
+
const files = includeFiles
|
|
37
|
+
? filtered.filter(e => e.isFile()).sort((a, b) => a.name.localeCompare(b.name))
|
|
38
|
+
: [];
|
|
39
|
+
const items = [...dirs, ...files];
|
|
40
|
+
for (let i = 0; i < items.length; i++) {
|
|
41
|
+
const entry = items[i];
|
|
42
|
+
const isLast = i === items.length - 1;
|
|
43
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
44
|
+
const icon = entry.isDirectory() ? '📁' : getFileIcon(entry.name);
|
|
45
|
+
lines.push(`${prefix}${connector}${icon} ${entry.name}${entry.isDirectory() ? '/' : ''}`);
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
48
|
+
await walkDir(path.join(dir, entry.name), newPrefix, depth + 1, maxDepth, exclude, includeFiles, lines);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function getFileIcon(filename) {
|
|
53
|
+
const ext = path.extname(filename).toLowerCase();
|
|
54
|
+
const icons = {
|
|
55
|
+
'.ts': '📘', '.tsx': '⚛️', '.js': '📙', '.jsx': '⚛️',
|
|
56
|
+
'.py': '🐍', '.vue': '💚', '.svelte': '🧡',
|
|
57
|
+
'.css': '🎨', '.scss': '🎨', '.sass': '🎨',
|
|
58
|
+
'.html': '📄', '.json': '📋', '.md': '📝',
|
|
59
|
+
'.yaml': '⚙️', '.yml': '⚙️', '.toml': '⚙️',
|
|
60
|
+
'.env': '🔐', '.lock': '🔒',
|
|
61
|
+
'.png': '🖼️', '.jpg': '🖼️', '.svg': '🖼️',
|
|
62
|
+
'.test.ts': '🧪', '.spec.ts': '🧪'
|
|
63
|
+
};
|
|
64
|
+
if (filename.includes('.test.') || filename.includes('.spec.'))
|
|
65
|
+
return '🧪';
|
|
66
|
+
return icons[ext] || '📄';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get a summary of the folder structure
|
|
70
|
+
*/
|
|
71
|
+
export async function getFolderSummary(rootDir) {
|
|
72
|
+
const byExtension = {};
|
|
73
|
+
let totalFiles = 0;
|
|
74
|
+
let totalDirs = 0;
|
|
75
|
+
const topLevelDirs = [];
|
|
76
|
+
async function walk(dir, isTopLevel) {
|
|
77
|
+
let entries;
|
|
78
|
+
try {
|
|
79
|
+
entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (DEFAULT_EXCLUDE.includes(entry.name) || entry.name.startsWith('.'))
|
|
86
|
+
continue;
|
|
87
|
+
if (entry.isDirectory()) {
|
|
88
|
+
totalDirs++;
|
|
89
|
+
if (isTopLevel)
|
|
90
|
+
topLevelDirs.push(entry.name);
|
|
91
|
+
await walk(path.join(dir, entry.name), false);
|
|
92
|
+
}
|
|
93
|
+
else if (entry.isFile()) {
|
|
94
|
+
totalFiles++;
|
|
95
|
+
const ext = path.extname(entry.name).toLowerCase() || 'no-ext';
|
|
96
|
+
byExtension[ext] = (byExtension[ext] || 0) + 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
await walk(rootDir, true);
|
|
101
|
+
return { totalFiles, totalDirs, byExtension, topLevelDirs };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=folder-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-tree.js","sourceRoot":"","sources":["../../src/utils/folder-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package Analyzer - Check for outdated packages and compatibility issues
|
|
3
|
+
*/
|
|
4
|
+
export interface PackageInfo {
|
|
5
|
+
name: string;
|
|
6
|
+
current: string;
|
|
7
|
+
wanted: string;
|
|
8
|
+
latest: string;
|
|
9
|
+
isOutdated: boolean;
|
|
10
|
+
isMajorUpdate: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface PackageAnalysis {
|
|
13
|
+
total: number;
|
|
14
|
+
outdated: PackageInfo[];
|
|
15
|
+
majorUpdates: PackageInfo[];
|
|
16
|
+
devDependencies: string[];
|
|
17
|
+
dependencies: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Analyze package.json for outdated packages
|
|
21
|
+
*/
|
|
22
|
+
export declare function analyzePackages(repoRoot: string, onProgress?: (message: string) => void): Promise<PackageAnalysis | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Format package analysis for report
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatPackageReport(analysis: PackageAnalysis): string;
|
|
27
|
+
//# sourceMappingURL=package-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-analyzer.d.ts","sourceRoot":"","sources":["../../src/utils/package-analyzer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACjC,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACvC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA6DjC;AASD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CA8BrE"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package Analyzer - Check for outdated packages and compatibility issues
|
|
3
|
+
*/
|
|
4
|
+
import { executeCommand } from '../core/executor.js';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Analyze package.json for outdated packages
|
|
9
|
+
*/
|
|
10
|
+
export async function analyzePackages(repoRoot, onProgress) {
|
|
11
|
+
const pkgPath = path.join(repoRoot, 'package.json');
|
|
12
|
+
if (!fs.existsSync(pkgPath)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
onProgress?.('🔍 Vanta is searching the internet for package updates...');
|
|
16
|
+
// Read package.json
|
|
17
|
+
let pkg;
|
|
18
|
+
try {
|
|
19
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const dependencies = Object.keys(pkg.dependencies || {});
|
|
25
|
+
const devDependencies = Object.keys(pkg.devDependencies || {});
|
|
26
|
+
// Run npm outdated
|
|
27
|
+
onProgress?.('🌐 Checking npm registry for latest versions...');
|
|
28
|
+
const result = await executeCommand('npm', ['outdated', '--json'], repoRoot, { timeout: 30000 });
|
|
29
|
+
const outdated = [];
|
|
30
|
+
const majorUpdates = [];
|
|
31
|
+
if (result.stdout) {
|
|
32
|
+
try {
|
|
33
|
+
const data = JSON.parse(result.stdout);
|
|
34
|
+
for (const [name, info] of Object.entries(data)) {
|
|
35
|
+
const pkgInfo = {
|
|
36
|
+
name,
|
|
37
|
+
current: info.current || 'N/A',
|
|
38
|
+
wanted: info.wanted || 'N/A',
|
|
39
|
+
latest: info.latest || 'N/A',
|
|
40
|
+
isOutdated: info.current !== info.latest,
|
|
41
|
+
isMajorUpdate: isMajorVersionDiff(info.current, info.latest)
|
|
42
|
+
};
|
|
43
|
+
if (pkgInfo.isOutdated) {
|
|
44
|
+
outdated.push(pkgInfo);
|
|
45
|
+
if (pkgInfo.isMajorUpdate) {
|
|
46
|
+
majorUpdates.push(pkgInfo);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// JSON parse error, npm outdated might have failed
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
onProgress?.(`✅ Package analysis complete (${outdated.length} updates available)`);
|
|
56
|
+
return {
|
|
57
|
+
total: dependencies.length + devDependencies.length,
|
|
58
|
+
outdated,
|
|
59
|
+
majorUpdates,
|
|
60
|
+
dependencies,
|
|
61
|
+
devDependencies
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function isMajorVersionDiff(current, latest) {
|
|
65
|
+
if (!current || !latest)
|
|
66
|
+
return false;
|
|
67
|
+
const currentMajor = parseInt(current.replace(/[^0-9.]/g, '').split('.')[0], 10);
|
|
68
|
+
const latestMajor = parseInt(latest.replace(/[^0-9.]/g, '').split('.')[0], 10);
|
|
69
|
+
return latestMajor > currentMajor;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Format package analysis for report
|
|
73
|
+
*/
|
|
74
|
+
export function formatPackageReport(analysis) {
|
|
75
|
+
let report = `## 📦 Package Analysis\n\n`;
|
|
76
|
+
report += `- **Total Packages**: ${analysis.total}\n`;
|
|
77
|
+
report += `- **Dependencies**: ${analysis.dependencies.length}\n`;
|
|
78
|
+
report += `- **Dev Dependencies**: ${analysis.devDependencies.length}\n`;
|
|
79
|
+
report += `- **Outdated**: ${analysis.outdated.length}\n`;
|
|
80
|
+
report += `- **Major Updates Available**: ${analysis.majorUpdates.length}\n\n`;
|
|
81
|
+
if (analysis.majorUpdates.length > 0) {
|
|
82
|
+
report += `### ⚠️ Major Updates Available\n\n`;
|
|
83
|
+
report += `| Package | Current | Latest | Action |\n`;
|
|
84
|
+
report += `|---------|---------|--------|--------|\n`;
|
|
85
|
+
for (const pkg of analysis.majorUpdates) {
|
|
86
|
+
report += `| ${pkg.name} | ${pkg.current} | ${pkg.latest} | Review breaking changes |\n`;
|
|
87
|
+
}
|
|
88
|
+
report += `\n`;
|
|
89
|
+
}
|
|
90
|
+
if (analysis.outdated.length > 0 && analysis.outdated.length !== analysis.majorUpdates.length) {
|
|
91
|
+
report += `### 📌 Minor/Patch Updates\n\n`;
|
|
92
|
+
const minorUpdates = analysis.outdated.filter(p => !p.isMajorUpdate);
|
|
93
|
+
for (const pkg of minorUpdates.slice(0, 10)) {
|
|
94
|
+
report += `- **${pkg.name}**: ${pkg.current} → ${pkg.latest}\n`;
|
|
95
|
+
}
|
|
96
|
+
if (minorUpdates.length > 10) {
|
|
97
|
+
report += `- ... and ${minorUpdates.length - 10} more\n`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return report;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=package-analyzer.js.map
|