Linter.utils.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. import {
  2. Box,
  3. Clock,
  4. Eye,
  5. Lock,
  6. LockIcon,
  7. Ruler,
  8. Scaling,
  9. Table2,
  10. TextSearch,
  11. Unlock,
  12. User,
  13. } from 'lucide-react'
  14. import Link from 'next/link'
  15. import { Badge, Button } from 'ui'
  16. import { asGraphqlExposureLint, GraphqlExposureLintCTA } from './GraphqlExposureLintCTA'
  17. import { LINTER_LEVELS, LintInfo } from '@/components/interfaces/Linter/Linter.constants'
  18. import { Lint, LINT_TYPES } from '@/data/lint/lint-query'
  19. import { DOCS_URL } from '@/lib/constants'
  20. export const lintInfoMap: LintInfo[] = [
  21. {
  22. name: 'unindexed_foreign_keys',
  23. title: 'Unindexed foreign keys',
  24. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  25. link: ({ projectRef, metadata }) =>
  26. `/project/${projectRef}/database/indexes?schema=${encodeURIComponent(metadata?.schema ?? '')}`,
  27. linkText: 'Create an index',
  28. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0001_unindexed_foreign_keys`,
  29. category: 'performance',
  30. },
  31. {
  32. name: 'auth_users_exposed',
  33. title: 'Exposed Auth Users',
  34. icon: <Lock className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  35. link: ({ projectRef }) => `/project/${projectRef}/editor`,
  36. linkText: 'View table',
  37. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0002_auth_users_exposed`,
  38. category: 'security',
  39. },
  40. {
  41. name: 'auth_rls_initplan',
  42. title: 'Auth RLS Initialization Plan',
  43. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  44. link: ({ projectRef }) => `/project/${projectRef}/auth/policies`,
  45. linkText: 'View policies',
  46. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0003_auth_rls_initplan`,
  47. category: 'performance',
  48. },
  49. {
  50. name: 'no_primary_key',
  51. title: 'No Primary Key',
  52. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  53. link: ({ projectRef }) => `/project/${projectRef}/editor`,
  54. linkText: 'View table',
  55. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0004_no_primary_key`,
  56. category: 'performance',
  57. },
  58. {
  59. name: 'unused_index',
  60. title: 'Unused Index',
  61. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  62. link: ({ projectRef, metadata }) =>
  63. `/project/${projectRef}/database/indexes?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
  64. linkText: 'View index',
  65. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0005_unused_index`,
  66. category: 'performance',
  67. },
  68. {
  69. name: 'multiple_permissive_policies',
  70. title: 'Multiple Permissive Policies',
  71. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  72. link: ({ projectRef, metadata }) =>
  73. `/project/${projectRef}/auth/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  74. linkText: 'View policies',
  75. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0006_multiple_permissive_policies`,
  76. category: 'performance',
  77. },
  78. {
  79. name: 'policy_exists_rls_disabled',
  80. title: 'Policy Exists RLS Disabled',
  81. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  82. link: ({ projectRef, metadata }) =>
  83. `/project/${projectRef}/auth/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  84. linkText: 'View policies',
  85. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0007_policy_exists_rls_disabled`,
  86. category: 'security',
  87. },
  88. {
  89. name: 'rls_enabled_no_policy',
  90. title: 'RLS Enabled No Policy',
  91. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  92. link: ({ projectRef, metadata }) =>
  93. `/project/${projectRef}/auth/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  94. linkText: 'View table',
  95. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0008_rls_enabled_no_policy`,
  96. category: 'security',
  97. },
  98. {
  99. name: 'duplicate_index',
  100. title: 'Duplicate Index',
  101. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  102. link: ({ projectRef, metadata }) =>
  103. `/project/${projectRef}/database/indexes?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
  104. linkText: 'View index',
  105. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0009_duplicate_index`,
  106. category: 'performance',
  107. },
  108. {
  109. name: 'security_definer_view',
  110. title: 'Security Definer View',
  111. icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  112. link: () =>
  113. `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0010_security_definer_view`,
  114. linkText: 'View docs',
  115. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0010_security_definer_view`,
  116. category: 'security',
  117. },
  118. {
  119. name: 'function_search_path_mutable',
  120. title: 'Function Search Path Mutable',
  121. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  122. link: ({ projectRef, metadata }) =>
  123. `/project/${projectRef}/database/functions?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  124. linkText: 'View functions',
  125. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0011_function_search_path_mutable`,
  126. category: 'security',
  127. },
  128. {
  129. name: 'auth_allow_anonymous_sign_ins',
  130. title: 'Anonymous Sign-Ins Allowed',
  131. icon: <User className="text-foreground-muted" size={15} strokeWidth={1} />,
  132. link: ({ projectRef }) => `/project/${projectRef}/auth/providers`,
  133. linkText: 'View settings',
  134. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0012_auth_allow_anonymous_sign_ins`,
  135. category: 'security',
  136. },
  137. {
  138. name: 'rls_disabled_in_public',
  139. title: 'RLS Disabled in Public',
  140. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  141. link: ({ projectRef, metadata }) =>
  142. `/project/${projectRef}/auth/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  143. linkText: 'View policies',
  144. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0013_rls_disabled_in_public`,
  145. category: 'security',
  146. },
  147. {
  148. name: 'extension_in_public',
  149. title: 'Extension in Public',
  150. icon: <Unlock className="text-foreground-muted" size={15} strokeWidth={1} />,
  151. link: ({ projectRef, metadata }) =>
  152. `/project/${projectRef}/database/extensions?filter=${encodeURIComponent(metadata?.name ?? '')}`,
  153. linkText: 'View extension',
  154. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0014_extension_in_public`,
  155. category: 'security',
  156. },
  157. {
  158. name: 'auth_otp_long_expiry',
  159. title: 'Auth OTP Long Expiry',
  160. icon: <Clock className="text-foreground-muted" size={15} strokeWidth={1} />,
  161. link: ({ projectRef }) => `/project/${projectRef}/auth/providers`,
  162. linkText: 'View settings',
  163. docsLink: `${DOCS_URL}/guides/platform/going-into-prod#security`,
  164. category: 'security',
  165. },
  166. {
  167. name: 'auth_otp_short_length',
  168. title: 'Auth OTP Short Length',
  169. icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
  170. link: ({ projectRef }) => `/project/${projectRef}/auth/providers`,
  171. linkText: 'View settings',
  172. docsLink: `${DOCS_URL}/guides/platform/going-into-prod#security`,
  173. category: 'security',
  174. },
  175. {
  176. name: 'auth_db_connections_absolute',
  177. title: 'Auth Absolute Connection Management Strategy',
  178. icon: <Scaling className="text-foreground-muted" size={15} strokeWidth={1} />,
  179. link: ({ projectRef }) => `/project/${projectRef}/auth/performance`,
  180. linkText: 'View settings',
  181. docsLink: `${DOCS_URL}/guides/platform/going-into-prod`,
  182. category: 'performance',
  183. },
  184. {
  185. name: 'rls_references_user_metadata',
  186. title: 'RLS references user metadata',
  187. icon: <User className="text-foreground-muted" size={15} strokeWidth={1} />,
  188. link: ({ projectRef }) => `/project/${projectRef}/auth/policies`,
  189. linkText: 'View policies',
  190. docsLink: `${DOCS_URL}/guides/database/database-linter?queryGroups=lint&lint=0015_rls_references_user_metadata`,
  191. category: 'security',
  192. },
  193. {
  194. name: 'materialized_view_in_api',
  195. title: 'Materialized View in API',
  196. icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  197. link: () => `${DOCS_URL}/guides/database/database-advisors?lint=0016_materialized_view_in_api`,
  198. linkText: 'View docs',
  199. docsLink: `${DOCS_URL}/guides/database/database-advisors?lint=0016_materialized_view_in_api`,
  200. category: 'security',
  201. },
  202. {
  203. name: 'foreign_table_in_api',
  204. title: 'Foreign Table in API',
  205. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  206. link: () => `${DOCS_URL}/guides/database/database-linter?lint=0017_foreign_table_in_api`,
  207. linkText: 'View docs',
  208. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0017_foreign_table_in_api`,
  209. category: 'security',
  210. },
  211. {
  212. name: 'unsupported_reg_types',
  213. title: 'Unsupported reg types',
  214. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  215. link: () =>
  216. `${DOCS_URL}/guides/database/database-advisors?lint=0018_unsupported_reg_types&queryGroups=lint`,
  217. linkText: 'View docs',
  218. docsLink: `${DOCS_URL}/guides/database/database-advisors?lint=0018_unsupported_reg_types&queryGroups=lint`,
  219. category: 'security',
  220. },
  221. {
  222. name: 'ssl_not_enforced',
  223. title: 'SSL not enforced',
  224. icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
  225. link: ({ projectRef }) => `/project/${projectRef}/database/settings`,
  226. linkText: 'View settings',
  227. docsLink: `${DOCS_URL}/guides/platform/ssl-enforcement`,
  228. category: 'security',
  229. },
  230. {
  231. name: 'network_restrictions_not_set',
  232. title: 'No network restrictions',
  233. icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
  234. link: ({ projectRef }) => `/project/${projectRef}/database/settings`,
  235. linkText: 'View settings',
  236. docsLink: `${DOCS_URL}/guides/platform/network-restrictions`,
  237. category: 'security',
  238. },
  239. {
  240. name: 'password_requirements_min_length',
  241. title: 'Minimum password length not set or inadequate',
  242. icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
  243. link: ({ projectRef }) => `/project/${projectRef}/auth/providers?provider=Email`,
  244. linkText: 'View settings',
  245. docsLink: `${DOCS_URL}/guides/platform/going-into-prod#security`,
  246. category: 'security',
  247. },
  248. {
  249. name: 'pitr_not_enabled',
  250. title: 'PITR not enabled',
  251. icon: <Ruler className="text-foreground-muted" size={15} strokeWidth={1} />,
  252. link: ({ projectRef }) => `/project/${projectRef}/database/backups/pitr`,
  253. linkText: 'View settings',
  254. docsLink: `${DOCS_URL}/guides/platform/backups#point-in-time-recovery`,
  255. category: 'security',
  256. },
  257. {
  258. name: 'auth_leaked_password_protection',
  259. title: 'Leaked Password Protection Disabled',
  260. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  261. link: ({ projectRef }) => `/project/${projectRef}/auth/providers?provider=Email`,
  262. linkText: 'View settings',
  263. docsLink: `${DOCS_URL}/guides/auth/password-security#password-strength-and-leaked-password-protection`,
  264. category: 'security',
  265. },
  266. {
  267. name: 'auth_insufficient_mfa_options',
  268. title: 'Insufficient MFA Options',
  269. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  270. link: ({ projectRef }) => `/project/${projectRef}/auth/mfa`,
  271. linkText: 'View settings',
  272. docsLink: `${DOCS_URL}/guides/auth/auth-mfa`,
  273. category: 'security',
  274. },
  275. {
  276. name: 'auth_password_policy_missing',
  277. title: 'Password Policy Missing',
  278. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  279. link: ({ projectRef }) => `/project/${projectRef}/auth/providers?provider=Email`,
  280. linkText: 'View settings',
  281. docsLink: `${DOCS_URL}/guides/auth/password-security`,
  282. category: 'security',
  283. },
  284. {
  285. name: 'leaked_service_key',
  286. title: 'Leaked Service Key Detected',
  287. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  288. link: ({ projectRef }) => `/project/${projectRef}/settings/api-keys`,
  289. linkText: 'View settings',
  290. docsLink: `${DOCS_URL}/guides/api/api-keys#the-servicerole-key`,
  291. category: 'security',
  292. },
  293. {
  294. name: 'no_backup_admin',
  295. title: 'No Backup Admin Detected',
  296. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  297. link: ({ projectRef }) => `/project/${projectRef}/auth/mfa`,
  298. linkText: 'View settings',
  299. docsLink: `${DOCS_URL}/guides/auth/auth-mfa`,
  300. category: 'security',
  301. },
  302. {
  303. name: 'vulnerable_postgres_version',
  304. title: 'Postgres version has security patches available',
  305. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  306. link: ({ projectRef }) => `/project/${projectRef}/settings/infrastructure`,
  307. linkText: 'View settings',
  308. docsLink: `${DOCS_URL}/guides/platform/upgrading`,
  309. category: 'security',
  310. },
  311. {
  312. name: 'sensitive_columns_exposed',
  313. title: 'Sensitive Columns Exposed',
  314. icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  315. link: ({ projectRef, metadata }) =>
  316. `/project/${projectRef}/editor?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
  317. linkText: 'View table',
  318. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0023_sensitive_columns_exposed`,
  319. category: 'security',
  320. },
  321. {
  322. name: 'rls_policy_always_true',
  323. title: 'RLS Policy Always True',
  324. icon: <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />,
  325. link: ({ projectRef, metadata }) =>
  326. `/project/${projectRef}/auth/policies?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  327. linkText: 'View policies',
  328. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0024_permissive_rls_policy`,
  329. category: 'security',
  330. },
  331. {
  332. name: 'public_bucket_allows_listing',
  333. title: 'Public Bucket Allows Listing',
  334. icon: <Box className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  335. link: ({ projectRef, metadata }) => {
  336. const bucketId = (metadata as Record<string, string | undefined> | undefined)?.bucket_id
  337. return `/project/${projectRef}/storage/files/buckets/${encodeURIComponent(bucketId ?? metadata?.name ?? '')}`
  338. },
  339. linkText: 'View bucket',
  340. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0025_public_bucket_allows_listing`,
  341. category: 'security',
  342. },
  343. {
  344. name: 'pg_graphql_anon_table_exposed',
  345. title: 'Public Can See Object in GraphQL Schema',
  346. icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  347. link: ({ projectRef, metadata }) =>
  348. `/project/${projectRef}/editor?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
  349. linkText: 'View object',
  350. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0026_pg_graphql_anon_table_exposed`,
  351. category: 'security',
  352. },
  353. {
  354. name: 'pg_graphql_authenticated_table_exposed',
  355. title: 'Signed-In Users Can See Object in GraphQL Schema',
  356. icon: <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />,
  357. link: ({ projectRef, metadata }) =>
  358. `/project/${projectRef}/editor?schema=${encodeURIComponent(metadata?.schema ?? '')}&table=${encodeURIComponent(metadata?.name ?? '')}`,
  359. linkText: 'View object',
  360. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0027_pg_graphql_authenticated_table_exposed`,
  361. category: 'security',
  362. },
  363. {
  364. name: 'anon_security_definer_function_executable',
  365. title: 'Public Can Execute SECURITY DEFINER Function',
  366. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  367. link: ({ projectRef, metadata }) =>
  368. `/project/${projectRef}/database/functions?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  369. linkText: 'View function',
  370. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0028_anon_security_definer_function_executable`,
  371. category: 'security',
  372. },
  373. {
  374. name: 'authenticated_security_definer_function_executable',
  375. title: 'Signed-In Users Can Execute SECURITY DEFINER Function',
  376. icon: <LockIcon className="text-foreground-muted" size={15} strokeWidth={1} />,
  377. link: ({ projectRef, metadata }) =>
  378. `/project/${projectRef}/database/functions?schema=${encodeURIComponent(metadata?.schema ?? '')}&search=${encodeURIComponent(metadata?.name ?? '')}`,
  379. linkText: 'View function',
  380. docsLink: `${DOCS_URL}/guides/database/database-linter?lint=0029_authenticated_security_definer_function_executable`,
  381. category: 'security',
  382. },
  383. ]
  384. export const LintCTA = ({
  385. title,
  386. projectRef,
  387. metadata,
  388. onAfterAction,
  389. }: {
  390. title: LINT_TYPES
  391. projectRef: string
  392. metadata: Lint['metadata']
  393. onAfterAction?: () => void
  394. }) => {
  395. const lintInfo = lintInfoMap.find((item) => item.name === title)
  396. if (!lintInfo) {
  397. return null
  398. }
  399. const graphqlExposureLintName = asGraphqlExposureLint(title)
  400. if (graphqlExposureLintName) {
  401. return (
  402. <GraphqlExposureLintCTA
  403. lintName={graphqlExposureLintName}
  404. projectRef={projectRef}
  405. metadata={metadata}
  406. onAfterAction={onAfterAction}
  407. />
  408. )
  409. }
  410. const link = lintInfo.link({ projectRef, metadata })
  411. const linkText = lintInfo.linkText
  412. return (
  413. <Button asChild type="default">
  414. <Link href={link} rel="noreferrer" className="no-underline">
  415. {linkText}
  416. </Link>
  417. </Button>
  418. )
  419. }
  420. export const EntityTypeIcon = ({ type }: { type: string | undefined }) => {
  421. switch (type) {
  422. case 'table':
  423. return <Table2 className="text-foreground-muted" size={15} strokeWidth={1} />
  424. case 'view':
  425. return <Eye className="text-foreground-muted" size={15} strokeWidth={1.5} />
  426. case 'auth':
  427. return <Lock className="text-foreground-muted" size={15} strokeWidth={1.5} />
  428. default:
  429. return <Box className="text-foreground-muted" size={15} strokeWidth={1.5} />
  430. }
  431. }
  432. export const LintEntity = ({ metadata }: { metadata: Lint['metadata'] }) => {
  433. return getLintEntityString(metadata)
  434. }
  435. export const LintCategoryBadge = ({ category }: { category: string }) => {
  436. return (
  437. <Badge variant={category === 'SECURITY' ? 'destructive' : 'warning'}>
  438. {category.toLowerCase()}
  439. </Badge>
  440. )
  441. }
  442. export const NoIssuesFound = ({ level }: { level: string }) => {
  443. const noun = level === LINTER_LEVELS.ERROR ? 'errors' : 'warnings'
  444. return (
  445. <div className="absolute top-28 px-6 flex flex-col items-center justify-center w-full gap-y-2">
  446. <TextSearch className="text-foreground-muted" strokeWidth={1} />
  447. <div className="text-center">
  448. <p className="text-foreground">No {noun} detected</p>
  449. <p className="text-foreground-light">
  450. Congrats! There are no {noun} detected for this database
  451. </p>
  452. </div>
  453. </div>
  454. )
  455. }
  456. export const createLintSummaryPrompt = (lint: Lint) => {
  457. const title = lintInfoMap.find((item) => item.name === lint.name)?.title ?? lint.title
  458. const entity = getLintEntityString(lint.metadata) || 'N/A'
  459. const schema = lint.metadata?.schema ?? 'N/A'
  460. const issue = lint.detail ? lint.detail.replace(/\\`/g, '`') : 'N/A'
  461. const description = lint.description ? lint.description.replace(/\\`/g, '`') : 'N/A'
  462. return `Summarize the issue and suggest fixes for the following lint item:
  463. Title: ${title}
  464. Entity: ${entity}
  465. Schema: ${schema}
  466. Issue Details: ${issue}
  467. Description: ${description}`
  468. }
  469. export const getLintEntityString = (metadata: Lint['metadata']) => {
  470. if (!metadata) {
  471. return undefined
  472. }
  473. if (metadata.entity) {
  474. return metadata.entity
  475. }
  476. if (metadata.schema && metadata.name) {
  477. const extendedMetadata = metadata as typeof metadata & { arguments?: string }
  478. const args =
  479. typeof extendedMetadata.arguments === 'string' ? extendedMetadata.arguments : undefined
  480. return `${metadata.schema}.${metadata.name}${args !== undefined ? `(${args})` : ''}`
  481. }
  482. return undefined
  483. }