MCPcopy
hub / github.com/cursor/community-plugins / runSecurityAgent

Function runSecurityAgent

apps/cursor/src/lib/plugins/scan.ts:427–548  ·  view source on GitHub ↗
(
  plugin: ScanInput,
  similar: SimilarPluginRow[],
)

Source from the content-addressed store, hash-verified

425}
426
427async function runSecurityAgent(
428 plugin: ScanInput,
429 similar: SimilarPluginRow[],
430): Promise<AgentVerdict> {
431 const tag = plugin.slug;
432 logInfo(tag, "runSecurityAgent start", {
433 components: plugin.components.length,
434 repository: plugin.repository,
435 similarCandidates: similar.length,
436 });
437
438 const apiKey = process.env.CURSOR_API_KEY;
439 if (!apiKey) {
440 throw new FatalScanError(
441 "CURSOR_API_KEY is not configured; cannot run plugin security scan.",
442 );
443 }
444
445 const repoMatch = plugin.repository
446 ? parseGitHubUrl(plugin.repository)
447 : null;
448
449 // The agent runs in `local` mode against a scratch dir on the function's
450 // filesystem: either a fresh clone of the user's public repo, or an empty
451 // dir when no repo URL was supplied. This deliberately avoids the cloud
452 // runtime's GitHub-App-scoped repo permissions, which would require every
453 // plugin submitter to install Cursor's GitHub App on their repo — not a
454 // workable UX for a public marketplace.
455 let cwd: string;
456 let cleanup: () => Promise<void>;
457 let hasRepo = false;
458 try {
459 if (repoMatch) {
460 const cloned = await cloneRepo(repoMatch.owner, repoMatch.repo, tag);
461 cwd = cloned.cwd;
462 cleanup = cloned.cleanup;
463 hasRepo = true;
464 } else {
465 cwd = await mkdtemp(path.join(tmpdir(), "plugin-scan-no-repo-"));
466 cleanup = () => rm(cwd, { recursive: true, force: true }).catch(() => {});
467 logInfo(tag, "no repo URL; using empty cwd", { cwd });
468 }
469 } catch (err) {
470 if (err instanceof UnscannableRepoError) {
471 logError(tag, "repo unscannable; returning suspicious verdict", err);
472 return {
473 verdict: "suspicious",
474 severity: "low",
475 categories: [],
476 reasons: ["repository_clone_failed"],
477 summary: `Could not fetch ${plugin.repository}: ${err.message} Manual review required.`,
478 runId: null,
479 };
480 }
481 // Transient failure (network, rate limit, GitHub 5xx): rethrow so the
482 // drain route leaves the message for pgmq to redeliver instead of
483 // permanently flagging the plugin over an infra hiccup.
484 logError(tag, "repo fetch failed; treating as retryable", err);

Callers 1

runPluginScanFunction · 0.85

Calls 8

parseGitHubUrlFunction · 0.90
cloneRepoFunction · 0.85
redirectSdkStateToTmpFunction · 0.85
buildPromptFunction · 0.85
parseVerdictFunction · 0.85
cleanupFunction · 0.85
logInfoFunction · 0.70
logErrorFunction · 0.70

Tested by

no test coverage detected