UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

88 lines (87 loc) 4.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.requireAuth = exports.buildAuthGuidance = exports.ensureRealSiteDomain = void 0; const auth_config_1 = require("../v2/utils/auth-config"); // siteDomain placeholder patterns — catches AI fabricating a fake site // (e.g. "https://meego.example.com") instead of asking the user. const SITE_DOMAIN_PLACEHOLDER_PATTERNS = [ { re: /example\.(com|org|net)/i, reason: 'example.com/.org/.net is a reserved documentation domain' }, { re: /\.(test|example|invalid|localhost)(?:[\/:]|$)/i, reason: 'RFC 2606 reserved TLD (never resolvable)' }, { re: /\byour-(domain|site|host|company|org)\b/i, reason: 'looks like a scaffolding placeholder' }, { re: /\b(placeholder|sample|mock|fake|dummy|foo\.bar)\b/i, reason: 'contains an obvious placeholder token' }, { re: /\/\/localhost(?:[:\/]|$)/i, reason: 'localhost is not a real Meego site' }, ]; /** * Reject obviously fake siteDomain values (placeholder patterns). Does NOT * verify reachability — AI can still invent a real-looking internal hostname. * But catches the common fabrication mode (example.com, your-domain, etc.). * On fail: stderr guidance + exit 1. */ function ensureRealSiteDomain(siteDomain) { validateSiteDomainNotPlaceholder(siteDomain); } exports.ensureRealSiteDomain = ensureRealSiteDomain; function validateSiteDomainNotPlaceholder(siteDomain) { for (const { re, reason } of SITE_DOMAIN_PLACEHOLDER_PATTERNS) { if (re.test(siteDomain)) { process.stderr.write([ `Refusing to proceed: siteDomain "${siteDomain}" looks like a placeholder.`, `Reason: ${reason}.`, '', 'Ask the user which Meego site they actually use. Common options:', ' - https://project.feishu.cn (飞书项目 / Feishu Project)', ' - https://meegle.com (Meegle)', ' - or the user\'s custom domain', '', 'Then re-run the command with the real --site-domain <url>.', ].join('\n') + '\n'); process.exit(1); } } } /** * Build a user-facing authentication guidance message for the given origin. * Format is AI-friendly: clean text with no log4js prefix, structured so the AI * can relay it verbatim to the user (or the user can copy the command directly). */ function buildAuthGuidance(origin) { return [ `Authentication required for ${origin}.`, '', 'Run ONE of the following to log in:', '', ' [A] Developer Token (permanent, recommended)', ` 1. Open ${origin}/openapp/settings and copy your Developer Token`, ' 2. Run:', ` lpm login --site-domain ${origin} --token <developer_token>`, '', ' [B] Device Code OAuth (temporary, auto-refresh)', ` lpm login --site-domain ${origin}`, ' Follow the browser prompt to complete authorization.', ].join('\n'); } exports.buildAuthGuidance = buildAuthGuidance; /** * Preflight credential check for commands that require user identity. * On failure: writes the guidance to stderr as plain text (bypassing log4js * formatting so command lines can be copied without prefix noise) and exits 1. * Token expiry / refresh is still handled in-flight by getToolAuthHeaders(). */ function requireAuth(siteDomain) { let origin; try { origin = new URL(siteDomain).origin; } catch (_a) { process.stderr.write(`Invalid siteDomain: ${siteDomain}\n`); process.exit(1); } // Hard防线:拒绝假站点,防 AI 默默填充 example.com 之类 validateSiteDomainNotPlaceholder(siteDomain); const profile = (0, auth_config_1.loadEffectiveProfile)(origin); if (!profile || !(0, auth_config_1.resolveToken)(profile)) { process.stderr.write(buildAuthGuidance(origin) + '\n'); process.exit(1); } } exports.requireAuth = requireAuth;