@vibecodetown/mcp-server 2.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/LICENSE +21 -0
- package/README.md +269 -0
- package/build/auth/gate.js +225 -0
- package/build/auth/index.js +55 -0
- package/build/auth/public_key.js +27 -0
- package/build/auth/token_cache.js +122 -0
- package/build/auth/token_verifier.js +103 -0
- package/build/bootstrap/doctor.js +115 -0
- package/build/bootstrap/installer.js +673 -0
- package/build/bootstrap/lock.js +37 -0
- package/build/bootstrap/platform.js +26 -0
- package/build/bootstrap/registry.js +37 -0
- package/build/cache/index.js +147 -0
- package/build/cli.js +101 -0
- package/build/contracts.js +22 -0
- package/build/control_plane/gate.js +161 -0
- package/build/control_plane/index.js +6 -0
- package/build/dx/activity.js +139 -0
- package/build/engine.js +106 -0
- package/build/errors.js +171 -0
- package/build/generated/activate_input.js +2 -0
- package/build/generated/activate_output.js +57 -0
- package/build/generated/advisory_review_input.js +2 -0
- package/build/generated/advisory_review_output.js +35 -0
- package/build/generated/auth_token_file.js +2 -0
- package/build/generated/briefing_input.js +2 -0
- package/build/generated/briefing_output.js +2 -0
- package/build/generated/clinic_bridge_file.js +13 -0
- package/build/generated/contracts_bundle_info.js +5 -0
- package/build/generated/create_work_order_input.js +2 -0
- package/build/generated/create_work_order_output.js +2 -0
- package/build/generated/current_work_order_file.js +2 -0
- package/build/generated/doctor_input.js +2 -0
- package/build/generated/doctor_output.js +24 -0
- package/build/generated/execution_result.js +2 -0
- package/build/generated/execution_task.js +2 -0
- package/build/generated/export_output_input.js +2 -0
- package/build/generated/export_output_output.js +2 -0
- package/build/generated/finalize_work_input.js +2 -0
- package/build/generated/finalize_work_output.js +2 -0
- package/build/generated/gate_input.js +2 -0
- package/build/generated/gate_output.js +2 -0
- package/build/generated/gate_result_v1.js +2 -0
- package/build/generated/get_decision_input.js +2 -0
- package/build/generated/get_decision_output.js +13 -0
- package/build/generated/handoff_to_clinic.js +2 -0
- package/build/generated/index.js +75 -0
- package/build/generated/inspect_code_input.js +2 -0
- package/build/generated/inspect_code_output.js +13 -0
- package/build/generated/memory_retrieve_output.js +2 -0
- package/build/generated/memory_state_file.js +2 -0
- package/build/generated/memory_status_input.js +2 -0
- package/build/generated/memory_status_output.js +13 -0
- package/build/generated/memory_sync_input.js +2 -0
- package/build/generated/memory_sync_output.js +13 -0
- package/build/generated/plugin_result.js +2 -0
- package/build/generated/react_perf_check_patterns_input.js +2 -0
- package/build/generated/react_perf_check_patterns_output.js +2 -0
- package/build/generated/react_perf_generate_report_input.js +2 -0
- package/build/generated/react_perf_generate_report_output.js +2 -0
- package/build/generated/repair_plan_input.js +2 -0
- package/build/generated/repair_plan_output.js +2 -0
- package/build/generated/run_app_input.js +2 -0
- package/build/generated/run_app_output.js +2 -0
- package/build/generated/run_state_file.js +13 -0
- package/build/generated/scaffold_input.js +2 -0
- package/build/generated/scaffold_output.js +2 -0
- package/build/generated/search_oss_input.js +2 -0
- package/build/generated/search_oss_output.js +2 -0
- package/build/generated/selection_validation_result.js +2 -0
- package/build/generated/signal_agent_input.js +2 -0
- package/build/generated/spec_high_ask_queue_items_file.js +2 -0
- package/build/generated/spec_high_clinic_bridge_output.js +2 -0
- package/build/generated/spec_high_decision_draft_output.js +2 -0
- package/build/generated/spec_high_validate_output.js +2 -0
- package/build/generated/status_input.js +2 -0
- package/build/generated/status_output.js +2 -0
- package/build/generated/submit_decision_input.js +2 -0
- package/build/generated/submit_decision_output.js +2 -0
- package/build/generated/tool_error_output.js +2 -0
- package/build/generated/undo_last_task_input.js +2 -0
- package/build/generated/undo_last_task_output.js +2 -0
- package/build/generated/update_input.js +2 -0
- package/build/generated/update_output.js +2 -0
- package/build/generated/vibe_pm_inspection_result.js +2 -0
- package/build/generated/vibe_pm_report_markdown.js +2 -0
- package/build/generated/vibe_pm_verdict.js +2 -0
- package/build/generated/vibe_repo_config.js +2 -0
- package/build/generated/vibecoding_helper_answer_output.js +2 -0
- package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
- package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
- package/build/generated/work_order_v1.js +2 -0
- package/build/generated/zoekt_evidence_input.js +2 -0
- package/build/generated/zoekt_evidence_output.js +2 -0
- package/build/index.js +111 -0
- package/build/legacy_alias.js +65 -0
- package/build/local-mode/bash.js +61 -0
- package/build/local-mode/config.js +171 -0
- package/build/local-mode/git.js +33 -0
- package/build/local-mode/init.js +110 -0
- package/build/local-mode/paths.js +24 -0
- package/build/local-mode/templates.js +856 -0
- package/build/local-mode/work-order.js +41 -0
- package/build/resources/index.js +246 -0
- package/build/security/input-validator.js +119 -0
- package/build/security/path-policy.js +289 -0
- package/build/security/sandbox.js +228 -0
- package/build/tools/react_perf/check_patterns.js +172 -0
- package/build/tools/react_perf/generate_report.js +337 -0
- package/build/tools/react_perf/index.js +119 -0
- package/build/tools/react_perf/rules/advanced.js +325 -0
- package/build/tools/react_perf/rules/async.js +104 -0
- package/build/tools/react_perf/rules/bundle.js +101 -0
- package/build/tools/react_perf/rules/client.js +186 -0
- package/build/tools/react_perf/rules/index.js +74 -0
- package/build/tools/react_perf/rules/js.js +148 -0
- package/build/tools/react_perf/rules/rendering.js +166 -0
- package/build/tools/react_perf/rules/rerender.js +161 -0
- package/build/tools/react_perf/rules/server.js +141 -0
- package/build/tools/react_perf/types.js +127 -0
- package/build/tools/vibe_pm/activate.js +102 -0
- package/build/tools/vibe_pm/advisory_review.js +77 -0
- package/build/tools/vibe_pm/briefing.js +178 -0
- package/build/tools/vibe_pm/context.js +439 -0
- package/build/tools/vibe_pm/create_work_order.js +271 -0
- package/build/tools/vibe_pm/doc_status_gate.js +370 -0
- package/build/tools/vibe_pm/doctor.js +262 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
- package/build/tools/vibe_pm/export_output.js +135 -0
- package/build/tools/vibe_pm/finalize_work.js +393 -0
- package/build/tools/vibe_pm/gate.js +33 -0
- package/build/tools/vibe_pm/get_decision.js +281 -0
- package/build/tools/vibe_pm/index.js +593 -0
- package/build/tools/vibe_pm/inspect_code.js +828 -0
- package/build/tools/vibe_pm/intent/generator.js +294 -0
- package/build/tools/vibe_pm/intent/index.js +5 -0
- package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
- package/build/tools/vibe_pm/intent/types.js +70 -0
- package/build/tools/vibe_pm/intent/verifier.js +237 -0
- package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
- package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
- package/build/tools/vibe_pm/kce/preflight.js +232 -0
- package/build/tools/vibe_pm/local_memory.js +26 -0
- package/build/tools/vibe_pm/memory_status.js +82 -0
- package/build/tools/vibe_pm/memory_sync.js +134 -0
- package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
- package/build/tools/vibe_pm/modules/ensure.js +100 -0
- package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
- package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
- package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
- package/build/tools/vibe_pm/modules/repo_context.js +56 -0
- package/build/tools/vibe_pm/modules/research_v1.js +114 -0
- package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
- package/build/tools/vibe_pm/pm_language.js +222 -0
- package/build/tools/vibe_pm/repair_plan.js +199 -0
- package/build/tools/vibe_pm/run_app.js +597 -0
- package/build/tools/vibe_pm/run_app_podman.js +64 -0
- package/build/tools/vibe_pm/scaffold.js +550 -0
- package/build/tools/vibe_pm/search_oss.js +124 -0
- package/build/tools/vibe_pm/status.js +153 -0
- package/build/tools/vibe_pm/submit_decision.js +87 -0
- package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
- package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
- package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
- package/build/tools/vibe_pm/types.js +229 -0
- package/build/tools/vibe_pm/undo_last_task.js +163 -0
- package/build/tools/vibe_pm/update.js +146 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
- package/build/tools.js +269 -0
- package/build/version-check.js +239 -0
- package/build/vibe-cli.js +631 -0
- package/package.json +76 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/security/sandbox.ts
|
|
2
|
+
// Optional Docker sandbox for production environments
|
|
3
|
+
//
|
|
4
|
+
// Note: This is an optional security layer. The primary security
|
|
5
|
+
// is provided by input validation and path policy.
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
/**
|
|
8
|
+
* Default sandbox configuration (no sandboxing)
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_SANDBOX_CONFIG = {
|
|
11
|
+
mode: "none",
|
|
12
|
+
allowedPaths: [],
|
|
13
|
+
networkAccess: false,
|
|
14
|
+
memoryLimitMb: 512,
|
|
15
|
+
cpuLimit: 1,
|
|
16
|
+
timeoutMs: 120_000,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Check if Docker is available on the system
|
|
20
|
+
*/
|
|
21
|
+
export async function isDockerAvailable() {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const proc = spawn("docker", ["version"], {
|
|
24
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
25
|
+
});
|
|
26
|
+
proc.on("error", () => resolve(false));
|
|
27
|
+
proc.on("close", (code) => resolve(code === 0));
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if firejail is available on the system
|
|
32
|
+
*/
|
|
33
|
+
export async function isFirejailAvailable() {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
const proc = spawn("firejail", ["--version"], {
|
|
36
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
37
|
+
});
|
|
38
|
+
proc.on("error", () => resolve(false));
|
|
39
|
+
proc.on("close", (code) => resolve(code === 0));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Run command in sandbox environment
|
|
44
|
+
*
|
|
45
|
+
* @param cmd - Command to execute
|
|
46
|
+
* @param args - Command arguments
|
|
47
|
+
* @param config - Sandbox configuration
|
|
48
|
+
* @returns Execution result
|
|
49
|
+
*/
|
|
50
|
+
export async function runInSandbox(cmd, args, config = DEFAULT_SANDBOX_CONFIG) {
|
|
51
|
+
switch (config.mode) {
|
|
52
|
+
case "docker":
|
|
53
|
+
return runInDocker(cmd, args, config);
|
|
54
|
+
case "firejail":
|
|
55
|
+
return runInFirejail(cmd, args, config);
|
|
56
|
+
case "none":
|
|
57
|
+
default:
|
|
58
|
+
return runDirect(cmd, args, config);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Run command directly (no sandboxing)
|
|
63
|
+
*/
|
|
64
|
+
async function runDirect(cmd, args, config) {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
const proc = spawn(cmd, args, {
|
|
67
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
68
|
+
});
|
|
69
|
+
let stdout = "";
|
|
70
|
+
let stderr = "";
|
|
71
|
+
const timeout = setTimeout(() => {
|
|
72
|
+
try {
|
|
73
|
+
proc.kill("SIGKILL");
|
|
74
|
+
}
|
|
75
|
+
catch { }
|
|
76
|
+
resolve({ code: 124, stdout, stderr: stderr + "\n[SANDBOX TIMEOUT]" });
|
|
77
|
+
}, config.timeoutMs);
|
|
78
|
+
proc.stdout.on("data", (data) => {
|
|
79
|
+
stdout += data.toString();
|
|
80
|
+
});
|
|
81
|
+
proc.stderr.on("data", (data) => {
|
|
82
|
+
stderr += data.toString();
|
|
83
|
+
});
|
|
84
|
+
proc.on("close", (code) => {
|
|
85
|
+
clearTimeout(timeout);
|
|
86
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Run command in Docker sandbox
|
|
92
|
+
*
|
|
93
|
+
* Requirements:
|
|
94
|
+
* - Docker must be installed and running
|
|
95
|
+
* - vibecode/sandbox:latest image must exist
|
|
96
|
+
*/
|
|
97
|
+
async function runInDocker(cmd, args, config) {
|
|
98
|
+
const dockerArgs = [
|
|
99
|
+
"run",
|
|
100
|
+
"--rm",
|
|
101
|
+
// Network isolation
|
|
102
|
+
"--network",
|
|
103
|
+
config.networkAccess ? "bridge" : "none",
|
|
104
|
+
// Resource limits
|
|
105
|
+
"--memory",
|
|
106
|
+
`${config.memoryLimitMb}m`,
|
|
107
|
+
"--cpus",
|
|
108
|
+
String(config.cpuLimit),
|
|
109
|
+
// Security options
|
|
110
|
+
"--security-opt",
|
|
111
|
+
"no-new-privileges:true",
|
|
112
|
+
"--cap-drop",
|
|
113
|
+
"ALL",
|
|
114
|
+
// Read-only root filesystem
|
|
115
|
+
"--read-only",
|
|
116
|
+
];
|
|
117
|
+
// Mount allowed paths as read-only volumes
|
|
118
|
+
for (const allowedPath of config.allowedPaths) {
|
|
119
|
+
dockerArgs.push("-v", `${allowedPath}:${allowedPath}:ro`);
|
|
120
|
+
}
|
|
121
|
+
// Add tmpfs for /tmp if needed
|
|
122
|
+
dockerArgs.push("--tmpfs", "/tmp:rw,noexec,nosuid,size=100m");
|
|
123
|
+
// Image and command
|
|
124
|
+
dockerArgs.push("vibecode/sandbox:latest", cmd, ...args);
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
const proc = spawn("docker", dockerArgs, {
|
|
127
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
128
|
+
});
|
|
129
|
+
let stdout = "";
|
|
130
|
+
let stderr = "";
|
|
131
|
+
const timeout = setTimeout(() => {
|
|
132
|
+
try {
|
|
133
|
+
// Kill the docker container
|
|
134
|
+
spawn("docker", ["kill", proc.pid?.toString() ?? ""], {
|
|
135
|
+
stdio: "ignore",
|
|
136
|
+
});
|
|
137
|
+
proc.kill("SIGKILL");
|
|
138
|
+
}
|
|
139
|
+
catch { }
|
|
140
|
+
resolve({ code: 124, stdout, stderr: stderr + "\n[DOCKER TIMEOUT]" });
|
|
141
|
+
}, config.timeoutMs);
|
|
142
|
+
proc.stdout.on("data", (data) => {
|
|
143
|
+
stdout += data.toString();
|
|
144
|
+
});
|
|
145
|
+
proc.stderr.on("data", (data) => {
|
|
146
|
+
stderr += data.toString();
|
|
147
|
+
});
|
|
148
|
+
proc.on("close", (code) => {
|
|
149
|
+
clearTimeout(timeout);
|
|
150
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Run command in firejail sandbox (Linux only)
|
|
156
|
+
*
|
|
157
|
+
* Requirements:
|
|
158
|
+
* - firejail must be installed
|
|
159
|
+
*/
|
|
160
|
+
async function runInFirejail(cmd, args, config) {
|
|
161
|
+
const firejailArgs = [
|
|
162
|
+
// Disable network if not allowed
|
|
163
|
+
...(config.networkAccess ? [] : ["--net=none"]),
|
|
164
|
+
// Resource limits
|
|
165
|
+
"--rlimit-as=" + config.memoryLimitMb * 1024 * 1024,
|
|
166
|
+
// Security options
|
|
167
|
+
"--caps.drop=all",
|
|
168
|
+
"--nonewprivs",
|
|
169
|
+
"--seccomp",
|
|
170
|
+
// No X11
|
|
171
|
+
"--x11=none",
|
|
172
|
+
// Read-only paths
|
|
173
|
+
"--read-only=/",
|
|
174
|
+
];
|
|
175
|
+
// Whitelist allowed paths
|
|
176
|
+
for (const allowedPath of config.allowedPaths) {
|
|
177
|
+
firejailArgs.push(`--whitelist=${allowedPath}`);
|
|
178
|
+
}
|
|
179
|
+
// Allow /tmp for temporary files
|
|
180
|
+
firejailArgs.push("--private-tmp");
|
|
181
|
+
// Command to run
|
|
182
|
+
firejailArgs.push("--", cmd, ...args);
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
const proc = spawn("firejail", firejailArgs, {
|
|
185
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
186
|
+
});
|
|
187
|
+
let stdout = "";
|
|
188
|
+
let stderr = "";
|
|
189
|
+
const timeout = setTimeout(() => {
|
|
190
|
+
try {
|
|
191
|
+
proc.kill("SIGKILL");
|
|
192
|
+
}
|
|
193
|
+
catch { }
|
|
194
|
+
resolve({ code: 124, stdout, stderr: stderr + "\n[FIREJAIL TIMEOUT]" });
|
|
195
|
+
}, config.timeoutMs);
|
|
196
|
+
proc.stdout.on("data", (data) => {
|
|
197
|
+
stdout += data.toString();
|
|
198
|
+
});
|
|
199
|
+
proc.stderr.on("data", (data) => {
|
|
200
|
+
stderr += data.toString();
|
|
201
|
+
});
|
|
202
|
+
proc.on("close", (code) => {
|
|
203
|
+
clearTimeout(timeout);
|
|
204
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create sandbox configuration from environment
|
|
210
|
+
*/
|
|
211
|
+
export function getSandboxConfigFromEnv() {
|
|
212
|
+
const mode = (process.env.VIBECODE_SANDBOX_MODE ?? "none");
|
|
213
|
+
const networkAccess = process.env.VIBECODE_SANDBOX_NETWORK === "true";
|
|
214
|
+
const memoryLimitMb = parseInt(process.env.VIBECODE_SANDBOX_MEMORY ?? "512", 10);
|
|
215
|
+
const cpuLimit = parseFloat(process.env.VIBECODE_SANDBOX_CPU ?? "1");
|
|
216
|
+
const timeoutMs = parseInt(process.env.VIBECODE_SANDBOX_TIMEOUT ?? "120000", 10);
|
|
217
|
+
const allowedPaths = (process.env.VIBECODE_SANDBOX_PATHS ?? "")
|
|
218
|
+
.split(":")
|
|
219
|
+
.filter((p) => p.length > 0);
|
|
220
|
+
return {
|
|
221
|
+
mode,
|
|
222
|
+
allowedPaths,
|
|
223
|
+
networkAccess,
|
|
224
|
+
memoryLimitMb,
|
|
225
|
+
cpuLimit,
|
|
226
|
+
timeoutMs,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/react_perf/check_patterns.ts
|
|
2
|
+
// Main implementation for react_perf.check_patterns
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { filterByMinImpact, sortByImpact, countViolationsByImpact, getTopSuggestions } from "./types.js";
|
|
6
|
+
import { getAllRules, getRulesByCategories } from "./rules/index.js";
|
|
7
|
+
// ============================================================
|
|
8
|
+
// File Discovery
|
|
9
|
+
// ============================================================
|
|
10
|
+
const DEFAULT_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
11
|
+
const IGNORE_DIRS = ["node_modules", ".next", "dist", "build", ".git", "coverage"];
|
|
12
|
+
function shouldIgnoreDir(dirName) {
|
|
13
|
+
return IGNORE_DIRS.includes(dirName) || dirName.startsWith(".");
|
|
14
|
+
}
|
|
15
|
+
function shouldIncludeFile(filePath) {
|
|
16
|
+
const ext = path.extname(filePath);
|
|
17
|
+
return DEFAULT_EXTENSIONS.includes(ext);
|
|
18
|
+
}
|
|
19
|
+
function findFiles(dir, files = []) {
|
|
20
|
+
if (!fs.existsSync(dir)) {
|
|
21
|
+
return files;
|
|
22
|
+
}
|
|
23
|
+
const stat = fs.statSync(dir);
|
|
24
|
+
if (stat.isFile()) {
|
|
25
|
+
if (shouldIncludeFile(dir)) {
|
|
26
|
+
files.push(dir);
|
|
27
|
+
}
|
|
28
|
+
return files;
|
|
29
|
+
}
|
|
30
|
+
if (!stat.isDirectory()) {
|
|
31
|
+
return files;
|
|
32
|
+
}
|
|
33
|
+
const entries = fs.readdirSync(dir);
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
if (shouldIgnoreDir(entry))
|
|
36
|
+
continue;
|
|
37
|
+
const fullPath = path.join(dir, entry);
|
|
38
|
+
const entryStat = fs.statSync(fullPath);
|
|
39
|
+
if (entryStat.isDirectory()) {
|
|
40
|
+
findFiles(fullPath, files);
|
|
41
|
+
}
|
|
42
|
+
else if (entryStat.isFile() && shouldIncludeFile(fullPath)) {
|
|
43
|
+
files.push(fullPath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return files;
|
|
47
|
+
}
|
|
48
|
+
function findPatternMatches(content, pattern) {
|
|
49
|
+
const matches = [];
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
// Use multiline matching
|
|
52
|
+
const globalPattern = new RegExp(pattern.source, pattern.flags + (pattern.flags.includes("g") ? "" : "g"));
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = globalPattern.exec(content)) !== null) {
|
|
55
|
+
// Calculate line number from index
|
|
56
|
+
const beforeMatch = content.substring(0, match.index);
|
|
57
|
+
const lineNumber = beforeMatch.split("\n").length;
|
|
58
|
+
const lastNewline = beforeMatch.lastIndexOf("\n");
|
|
59
|
+
const column = match.index - lastNewline;
|
|
60
|
+
// Get the line content for snippet
|
|
61
|
+
const lineText = lines[lineNumber - 1] || "";
|
|
62
|
+
matches.push({
|
|
63
|
+
line: lineNumber,
|
|
64
|
+
column,
|
|
65
|
+
text: lineText.trim().substring(0, 100) // Truncate long lines
|
|
66
|
+
});
|
|
67
|
+
// Prevent infinite loops on zero-length matches
|
|
68
|
+
if (match[0].length === 0) {
|
|
69
|
+
globalPattern.lastIndex++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return matches;
|
|
73
|
+
}
|
|
74
|
+
function checkAntiPattern(content, antiPattern) {
|
|
75
|
+
if (!antiPattern)
|
|
76
|
+
return false;
|
|
77
|
+
return antiPattern.test(content);
|
|
78
|
+
}
|
|
79
|
+
// ============================================================
|
|
80
|
+
// Main Analysis
|
|
81
|
+
// ============================================================
|
|
82
|
+
function analyzeFile(filePath, rules) {
|
|
83
|
+
const violations = [];
|
|
84
|
+
let content;
|
|
85
|
+
try {
|
|
86
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return violations;
|
|
90
|
+
}
|
|
91
|
+
for (const rule of rules) {
|
|
92
|
+
// Check file filter
|
|
93
|
+
if (rule.fileFilter && !rule.fileFilter.test(filePath)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// Check anti-pattern (if file has anti-pattern, skip this rule)
|
|
97
|
+
if (checkAntiPattern(content, rule.antiPattern)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
// Find pattern matches
|
|
101
|
+
const matches = findPatternMatches(content, rule.pattern);
|
|
102
|
+
for (const match of matches) {
|
|
103
|
+
violations.push({
|
|
104
|
+
rule_id: rule.id,
|
|
105
|
+
category: rule.category,
|
|
106
|
+
file: filePath,
|
|
107
|
+
line: match.line,
|
|
108
|
+
column: match.column,
|
|
109
|
+
impact: rule.impact,
|
|
110
|
+
message: rule.message,
|
|
111
|
+
suggestion: rule.suggestion,
|
|
112
|
+
code_snippet: match.text
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return violations;
|
|
117
|
+
}
|
|
118
|
+
// ============================================================
|
|
119
|
+
// Main Entry Point
|
|
120
|
+
// ============================================================
|
|
121
|
+
export async function checkPatterns(input) {
|
|
122
|
+
const startTime = Date.now();
|
|
123
|
+
// Determine target paths
|
|
124
|
+
const targetPaths = input.target_paths || [process.cwd()];
|
|
125
|
+
// Collect all files to scan
|
|
126
|
+
const allFiles = [];
|
|
127
|
+
for (const targetPath of targetPaths) {
|
|
128
|
+
const resolvedPath = path.resolve(targetPath);
|
|
129
|
+
const files = findFiles(resolvedPath);
|
|
130
|
+
allFiles.push(...files);
|
|
131
|
+
}
|
|
132
|
+
// Deduplicate files
|
|
133
|
+
const uniqueFiles = [...new Set(allFiles)];
|
|
134
|
+
// Get rules to check
|
|
135
|
+
const rules = input.rules
|
|
136
|
+
? getRulesByCategories(input.rules)
|
|
137
|
+
: getAllRules();
|
|
138
|
+
// Skip if no rules
|
|
139
|
+
if (rules.length === 0) {
|
|
140
|
+
return {
|
|
141
|
+
status: "OK",
|
|
142
|
+
total_files_scanned: uniqueFiles.length,
|
|
143
|
+
total_violations: 0,
|
|
144
|
+
violations: [],
|
|
145
|
+
summary: { critical: 0, high: 0, medium: 0, low: 0 },
|
|
146
|
+
top_suggestions: [],
|
|
147
|
+
scan_time_ms: Date.now() - startTime
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Analyze all files
|
|
151
|
+
let allViolations = [];
|
|
152
|
+
for (const file of uniqueFiles) {
|
|
153
|
+
const fileViolations = analyzeFile(file, rules);
|
|
154
|
+
allViolations.push(...fileViolations);
|
|
155
|
+
}
|
|
156
|
+
// Filter by minimum impact
|
|
157
|
+
const minImpact = input.min_impact || "MEDIUM";
|
|
158
|
+
allViolations = filterByMinImpact(allViolations, minImpact);
|
|
159
|
+
// Sort by impact
|
|
160
|
+
allViolations = sortByImpact(allViolations);
|
|
161
|
+
// Build output
|
|
162
|
+
const output = {
|
|
163
|
+
status: allViolations.length > 0 ? "VIOLATIONS_FOUND" : "OK",
|
|
164
|
+
total_files_scanned: uniqueFiles.length,
|
|
165
|
+
total_violations: allViolations.length,
|
|
166
|
+
violations: allViolations,
|
|
167
|
+
summary: countViolationsByImpact(allViolations),
|
|
168
|
+
top_suggestions: getTopSuggestions(allViolations, 3),
|
|
169
|
+
scan_time_ms: Date.now() - startTime
|
|
170
|
+
};
|
|
171
|
+
return output;
|
|
172
|
+
}
|