Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion apps/kimi-code/src/cli/sub/plugin-run-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { realpath } from 'node:fs/promises';
import path from 'node:path';
import { pathToFileURL } from 'node:url';

const ALLOWED_EXTENSIONS = ['.js', '.mjs', '.cjs'];

export async function runPluginNodeEntry(entry: string, args: readonly string[]): Promise<void> {
const pluginRoot = process.env['KIMI_PLUGIN_ROOT'];
if (pluginRoot === undefined || pluginRoot.trim().length === 0) {
Expand All @@ -16,11 +18,21 @@ export async function runPluginNodeEntry(entry: string, args: readonly string[])
throw new Error(`Plugin node entry must be inside KIMI_PLUGIN_ROOT: ${entry}`);
}

const ext = path.extname(entryReal).toLowerCase();
if (!ALLOWED_EXTENSIONS.includes(ext)) {
throw new Error(`Plugin node entry must have a valid extension (${ALLOWED_EXTENSIONS.join(', ')}): ${entry}`);
Comment on lines +21 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve extensionless Node entrypoints

When the native binary rewrites plugin MCP servers with command: "node", it passes the manifest's original args directly to __plugin_run_node (packages/agent-core/src/plugin/manager.ts:501-506), so this helper needs to accept the same script paths users can pass to Node. A valid plugin config such as args: ["./server"] or a direct package bin without a .js/.mjs/.cjs suffix previously ran from the plugin root, but now path.extname(entryReal) is "" and the MCP server fails before import. Please allow extensionless in-root scripts or otherwise emulate Node's accepted entrypoint behavior.

Useful? React with 👍 / 👎.

}

process.argv = [process.argv[0] ?? process.execPath, entryReal, ...args];
await import(pathToFileURL(entryReal).href);
}

function isWithin(candidate: string, root: string): boolean {
const relative = path.relative(root, candidate);
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
const isRelativeWithin = relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
if (!isRelativeWithin) {
return false;
}
const normalizedRoot = path.normalize(root + path.sep);
return candidate.startsWith(normalizedRoot);
}
5 changes: 3 additions & 2 deletions apps/kimi-code/src/feedback/archive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mkdir, mkdtemp, readdir, rm, stat } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { basename, dirname, join } from 'node:path';

import { getCacheDir } from '../utils/paths';

Expand Down Expand Up @@ -64,7 +64,8 @@ async function createArchivePath(filename: string): Promise<string> {
const root = join(getCacheDir(), 'feedback-uploads');
await mkdir(root, { recursive: true });
const dir = await mkdtemp(join(root, 'upload-'));
return join(dir, filename);
const safeFilename = basename(filename).replace(/[^a-zA-Z0-9._-]/g, '');
return join(dir, safeFilename);
}

function archivePathCleanupDir(archivePath: string): string {
Expand Down