UNPKG

ubon

Version:

Security scanner for AI-generated apps (Cursor, Lovable, Windsurf, v0). Catches hardcoded secrets, prompt injection, hallucinated imports, Server Actions / Edge runtime mistakes, and the vibe-coded vulnerabilities traditional linters miss.

124 lines (118 loc) 4.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.installAgentHarness = installAgentHarness; const fs_1 = require("fs"); const path_1 = require("path"); const hooks_1 = require("./hooks"); const hooks_2 = require("../utils/hooks"); const CURSOR_RULE = `--- description: Ubon security scanner integration globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.svelte", "**/*.astro"] --- # Ubon - Use \`ubon verify\` before shipping agent-generated changes. - Use \`ubon check --preset agent\` for fast local feedback. - Use \`ubon review --since origin/main\` before opening a PR. - Treat high-severity findings with confidence >= 0.85 as blockers unless explicitly suppressed with a reason. `; const AGENTS_SECTION = `# Agent guidance ## Ubon - Run \`ubon verify\` before considering implementation work complete. - For fast inner-loop checks, run \`ubon check --preset agent\`. - For PR review, run \`ubon review --since origin/main\`. - Do not ignore high-severity Ubon findings unless there is an explicit suppression reason. `; const GITHUB_WORKFLOW = `name: Ubon on: pull_request: branches: [main] push: branches: [main] jobs: verify: runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npx ubon@latest verify `; function shouldTarget(options, key) { return !!options.all || !!options[key]; } function writePlannedFile(file, force, wrote, skipped) { if ((0, fs_1.existsSync)(file.path) && !force) { skipped.push(file.path); return; } (0, fs_1.mkdirSync)((0, path_1.dirname)(file.path), { recursive: true }); (0, fs_1.writeFileSync)(file.path, file.content, 'utf-8'); if (file.mode !== undefined) { try { (0, fs_1.chmodSync)(file.path, file.mode); } catch { } } wrote.push(file.path); } function appendGitignore(directory, skipped) { const gitignore = (0, path_1.join)(directory, '.gitignore'); const line = '.ubon/'; const current = (0, fs_1.existsSync)(gitignore) ? (0, fs_1.readFileSync)(gitignore, 'utf-8') : ''; if (current.split(/\r?\n/).includes(line)) { skipped.push(gitignore); return null; } const content = `${current}${current.endsWith('\n') || current.length === 0 ? '' : '\n'}${line}\n`; const planned = { path: gitignore, content }; return planned; } function installAgentHarness(options) { const directory = options.directory; const dryRun = !options.write; const force = !!options.force; const wrote = []; const skipped = []; const plannedFiles = []; const anyTarget = options.all || options.cursor || options.claude || options.codex || options.preCommit || options.github; const targets = anyTarget ? options : { ...options, cursor: true }; if (shouldTarget(targets, 'cursor')) { plannedFiles.push({ path: (0, path_1.join)(directory, '.cursor/rules/ubon.mdc'), content: CURSOR_RULE }); } if (shouldTarget(targets, 'codex')) { plannedFiles.push({ path: (0, path_1.join)(directory, 'AGENTS.md'), content: AGENTS_SECTION }); } if (shouldTarget(targets, 'claude')) { plannedFiles.push({ path: (0, path_1.join)(directory, 'CLAUDE.md'), content: AGENTS_SECTION.replace('# Agent guidance', '# Claude Code guidance') }); } if (shouldTarget(targets, 'preCommit')) { plannedFiles.push({ path: (0, path_1.join)(directory, '.pre-commit-config.yaml'), content: (0, hooks_2.renderPreCommitConfig)({ mode: 'fast', failOn: 'error' }) }); } if (shouldTarget(targets, 'github')) { plannedFiles.push({ path: (0, path_1.join)(directory, '.github/workflows/ubon.yml'), content: GITHUB_WORKFLOW }); } const gitignorePlan = appendGitignore(directory, skipped); if (gitignorePlan) plannedFiles.push(gitignorePlan); if (!dryRun) { for (const file of plannedFiles) writePlannedFile(file, force, wrote, skipped); if (shouldTarget(targets, 'cursor')) { const cursor = (0, hooks_1.installCursorHooks)({ directory, cursor: true, force }); wrote.push(...cursor.wrote); skipped.push(...cursor.skipped); } } return { planned: plannedFiles.map((file) => file.path), wrote, skipped }; } //# sourceMappingURL=agent.js.map