mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
chore(studio): remove require-safe-sql-fragment ESLint rule (#46079)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Refactor / chore. ## What is the current behavior? A custom type-aware ESLint rule (`studio/require-safe-sql-fragment`) enforces that the `sql` argument to `executeSql` is a `SafeSqlFragment`. It runs in a separate `eslint.type-checks.config.cjs` and a dedicated CI ratchet step, and pulls in `@typescript-eslint/utils` as a direct dev dependency. ## What is the new behavior? `SafeSqlFragment` enforcement is now handled entirely by TypeScript compilation. The ESLint rule, its dedicated config, the ratchet baselines for it, the CI step, and the `@typescript-eslint/utils` direct dev dependency have all been removed. ## Additional context <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Simplified development linting workflow by removing type-aware ESLint checks and associated rule files. * Cleaned up ESLint configuration and dependencies in the studio application. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46079?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
1e113a8a01
commit
cc0b2d3d21
6 files changed
+1
-260
No files matched your search
@@ -44,8 +44,3 @@ jobs:
|
||||
|
||||
- name: Run ratchet script
|
||||
run: pnpm --filter studio run lint:ratchet
|
||||
|
||||
- name: Run type-aware ratchet script
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
run: pnpm --filter studio run lint:ratchet:type-checks
|
||||
+1
-144
@@ -6,8 +6,7 @@
|
||||
"@typescript-eslint/no-explicit-any": 990,
|
||||
"no-restricted-imports": 0,
|
||||
"no-restricted-exports": 251,
|
||||
"react/no-unstable-nested-components": 51,
|
||||
"studio/require-safe-sql-fragment": 158
|
||||
"react/no-unstable-nested-components": 51
|
||||
},
|
||||
"ruleFiles": {
|
||||
"react-hooks/exhaustive-deps": {
|
||||
@@ -930,148 +929,6 @@
|
||||
"components/ui/ErrorBoundary/ErrorBoundary.tsx": 1,
|
||||
"components/ui/NoPermission.tsx": 1,
|
||||
"pages/project/[ref]/observability/database.tsx": 1
|
||||
},
|
||||
"studio/require-safe-sql-fragment": {
|
||||
"components/interfaces/Auth/Hooks/CreateHookSheet.tsx": 1,
|
||||
"components/interfaces/Auth/Hooks/HooksListing.tsx": 1,
|
||||
"components/interfaces/Docs/Description.tsx": 1,
|
||||
"components/interfaces/Integrations/CronJobs/CronJobsTab.useCleanupActions.ts": 2,
|
||||
"components/interfaces/QueryPerformance/IndexAdvisor/index-advisor.utils.ts": 1,
|
||||
"components/interfaces/QueryPerformance/WithStatements/WithStatements.tsx": 1,
|
||||
"components/interfaces/QueryPerformance/useQueryPerformanceQuery.ts": 1,
|
||||
"components/interfaces/Reports/ReportBlock/ReportBlock.tsx": 1,
|
||||
"components/interfaces/Settings/API/DataApiEnableSwitch.utils.ts": 1,
|
||||
"components/interfaces/Storage/PublicBucketWarning.tsx": 1,
|
||||
"components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx": 12,
|
||||
"components/ui/AIAssistantPanel/DisplayBlockRenderer.tsx": 1,
|
||||
"components/ui/EditorPanel/EditorPanel.tsx": 1,
|
||||
"data/api-settings/create-and-expose-api-schema-mutation.ts": 1,
|
||||
"data/auth/index-worker-status-query.ts": 1,
|
||||
"data/auth/user-query.ts": 1,
|
||||
"data/auth/user-search-indexes-query.ts": 1,
|
||||
"data/auth/users-count-query.ts": 1,
|
||||
"data/auth/users-infinite-query.ts": 1,
|
||||
"data/config/disk-breakdown-query.ts": 1,
|
||||
"data/database-columns/database-column-create-mutation.ts": 1,
|
||||
"data/database-columns/database-column-delete-mutation.ts": 1,
|
||||
"data/database-columns/database-column-update-mutation.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-job-query.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-job-run-mutation.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-count-estimate-query.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-count-query.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-create-mutation.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-delete-mutation.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-infinite-query.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-minimal-infinite-query.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-jobs-toggle-mutation.ts": 1,
|
||||
"data/database-cron-jobs/database-cron-timezone-query.ts": 1,
|
||||
"data/database-cron-jobs/schedule-clean-up-mutation.ts": 1,
|
||||
"data/database-event-triggers/database-event-trigger-create-mutation.ts": 1,
|
||||
"data/database-event-triggers/database-event-trigger-delete-mutation.ts": 1,
|
||||
"data/database-event-triggers/database-event-triggers-query.ts": 1,
|
||||
"data/database-extensions/database-extension-disable-mutation.ts": 1,
|
||||
"data/database-extensions/database-extension-enable-mutation.ts": 1,
|
||||
"data/database-extensions/database-extensions-query.ts": 1,
|
||||
"data/database-functions/database-functions-create-mutation.ts": 1,
|
||||
"data/database-functions/database-functions-delete-mutation.ts": 1,
|
||||
"data/database-functions/database-functions-query.ts": 1,
|
||||
"data/database-functions/database-functions-update-mutation.ts": 1,
|
||||
"data/database-indexes/index-create-mutation.ts": 1,
|
||||
"data/database-indexes/index-delete-mutation.ts": 1,
|
||||
"data/database-indexes/indexes-query.ts": 1,
|
||||
"data/database-integrations/stripe/sync-state-query.ts": 1,
|
||||
"data/database-policies/database-policy-create-mutation.ts": 1,
|
||||
"data/database-policies/database-policy-delete-mutation.ts": 1,
|
||||
"data/database-policies/database-policy-update-mutation.ts": 1,
|
||||
"data/database-publications/database-publications-create-mutation.ts": 1,
|
||||
"data/database-publications/database-publications-update-mutation.ts": 1,
|
||||
"data/database-queues/database-queue-messages-archive-mutation.ts": 1,
|
||||
"data/database-queues/database-queue-messages-delete-mutation.ts": 1,
|
||||
"data/database-queues/database-queue-messages-infinite-query.ts": 1,
|
||||
"data/database-queues/database-queue-messages-read-mutation.ts": 1,
|
||||
"data/database-queues/database-queue-messages-send-mutation.ts": 1,
|
||||
"data/database-queues/database-queues-create-mutation.ts": 1,
|
||||
"data/database-queues/database-queues-delete-mutation.ts": 1,
|
||||
"data/database-queues/database-queues-expose-postgrest-status-query.ts": 1,
|
||||
"data/database-queues/database-queues-metrics-query.ts": 2,
|
||||
"data/database-queues/database-queues-purge-mutation.ts": 1,
|
||||
"data/database-queues/database-queues-query.ts": 1,
|
||||
"data/database-queues/database-queues-toggle-postgrest-mutation.ts": 1,
|
||||
"data/database-roles/database-role-create-mutation.ts": 1,
|
||||
"data/database-roles/database-role-delete-mutation.ts": 1,
|
||||
"data/database-roles/database-role-update-mutation.ts": 1,
|
||||
"data/database-roles/database-roles-query.ts": 1,
|
||||
"data/database-triggers/database-trigger-create-mutation.ts": 1,
|
||||
"data/database-triggers/database-trigger-delete-mutation.ts": 1,
|
||||
"data/database-triggers/database-trigger-update-mutation.ts": 1,
|
||||
"data/database-triggers/database-trigger-update-transaction-mutation.ts": 1,
|
||||
"data/database/constraints-query.ts": 1,
|
||||
"data/database/database-size-query.ts": 1,
|
||||
"data/database/entity-definitions-query.ts": 1,
|
||||
"data/database/foreign-key-constraints-query.ts": 1,
|
||||
"data/database/keywords-query.ts": 1,
|
||||
"data/database/max-connections-query.ts": 1,
|
||||
"data/database/migrations-query.ts": 1,
|
||||
"data/database/retrieve-index-advisor-result-query.ts": 1,
|
||||
"data/database/retrieve-index-from-select-query.ts": 2,
|
||||
"data/database/schema-create-mutation.ts": 1,
|
||||
"data/database/schemas-query.ts": 1,
|
||||
"data/database/supamonitor-enabled-query.ts": 1,
|
||||
"data/database/table-columns-query.ts": 1,
|
||||
"data/database/table-definition-query.ts": 1,
|
||||
"data/database/table-index-advisor-query.ts": 1,
|
||||
"data/database/view-definition-query.ts": 1,
|
||||
"data/entity-types/entity-types-infinite-query.ts": 1,
|
||||
"data/enumerated-types/enumerated-type-create-mutation.ts": 1,
|
||||
"data/enumerated-types/enumerated-type-delete-mutation.ts": 1,
|
||||
"data/enumerated-types/enumerated-type-update-mutation.ts": 1,
|
||||
"data/fdw/fdw-create-mutation.ts": 1,
|
||||
"data/fdw/fdw-delete-mutation.ts": 1,
|
||||
"data/fdw/fdw-drop-foreign-table-mutation.ts": 1,
|
||||
"data/fdw/fdw-import-foreign-schema-mutation.ts": 1,
|
||||
"data/fdw/fdw-update-mutation.ts": 1,
|
||||
"data/fdw/fdws-query.ts": 1,
|
||||
"data/privileges/default-privileges-query.ts": 1,
|
||||
"data/privileges/exposed-function-counts-query.ts": 1,
|
||||
"data/privileges/exposed-functions-infinite-query.ts": 1,
|
||||
"data/privileges/exposed-table-counts-query.ts": 1,
|
||||
"data/privileges/exposed-tables-infinite-query.ts": 1,
|
||||
"data/privileges/table-api-access-mutation.ts": 1,
|
||||
"data/privileges/table-privileges-grant-mutation.ts": 1,
|
||||
"data/privileges/table-privileges-query.ts": 1,
|
||||
"data/privileges/table-privileges-revoke-mutation.ts": 1,
|
||||
"data/privileges/update-default-privileges-mutation.ts": 1,
|
||||
"data/privileges/update-exposed-entities-mutation.ts": 1,
|
||||
"data/read-replicas/replica-lag-query.ts": 1,
|
||||
"data/sql/abort-query-mutation.ts": 1,
|
||||
"data/sql/execute-sql-query.ts": 1,
|
||||
"data/sql/ongoing-queries-query.ts": 1,
|
||||
"data/storage/bucket-prefix-delete-mutation.ts": 1,
|
||||
"data/storage/buckets-max-size-limit-query.ts": 2,
|
||||
"data/storage/public-buckets-with-select-policies-query.ts": 1,
|
||||
"data/table-editor/table-editor-query.ts": 1,
|
||||
"data/table-rows/get-cell-value-mutation.ts": 1,
|
||||
"data/table-rows/operation-queue-save-mutation.ts": 1,
|
||||
"data/table-rows/table-row-create-mutation.ts": 1,
|
||||
"data/table-rows/table-row-delete-all-mutation.ts": 1,
|
||||
"data/table-rows/table-row-delete-mutation.tsx": 1,
|
||||
"data/table-rows/table-row-truncate-mutation.ts": 1,
|
||||
"data/table-rows/table-row-update-mutation.ts": 1,
|
||||
"data/table-rows/table-rows-count-query.ts": 1,
|
||||
"data/table-rows/table-rows-query.ts": 3,
|
||||
"data/tables/table-names-query.ts": 1,
|
||||
"data/tables/tables-roles-access-query.ts": 1,
|
||||
"data/vault/vault-secret-create-mutation.ts": 1,
|
||||
"data/vault/vault-secret-decrypted-value-query.ts": 2,
|
||||
"data/vault/vault-secret-delete-mutation.ts": 1,
|
||||
"data/vault/vault-secret-update-mutation.ts": 1,
|
||||
"data/vault/vault-secrets-query.ts": 1,
|
||||
"hooks/analytics/useDbQuery.tsx": 1,
|
||||
"lib/ai/tools/fallback-tools.ts": 1,
|
||||
"pages/api/ai/code/complete.ts": 1,
|
||||
"pages/api/ai/sql/generate-v4.ts": 1,
|
||||
"state/role-impersonation-state.tsx": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* ESLint rule to require SafeSqlFragment (not a plain string) for the sql argument of executeSql.
|
||||
*
|
||||
* During migration, executeSql still accepts string (a supertype of SafeSqlFragment), but new
|
||||
* call sites should use SafeSqlFragment to avoid SQL injection risks and to be ready for when
|
||||
* the type is tightened.
|
||||
*
|
||||
* BAD:
|
||||
* executeSql({ projectRef, sql: `SELECT * FROM ${table}` })
|
||||
* const query = buildQuery() // returns string
|
||||
* executeSql({ projectRef, sql: query })
|
||||
*
|
||||
* GOOD:
|
||||
* executeSql({ projectRef, sql: safeSql`SELECT * FROM ${ident(table)}` })
|
||||
* executeSql({ projectRef, sql: keyword('SELECT 1') })
|
||||
*/
|
||||
|
||||
const { ESLintUtils } = require('@typescript-eslint/utils')
|
||||
|
||||
/** @type {import('@typescript-eslint/utils').TSESLint.RuleModule<'requireSafeSqlFragment'>} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description:
|
||||
'Require executeSql to be called with SafeSqlFragment, not a plain string. Use safeSql`...`, ident(), literal(), or keyword() to create a SafeSqlFragment.',
|
||||
recommended: true,
|
||||
},
|
||||
messages: {
|
||||
requireSafeSqlFragment:
|
||||
'The sql argument to executeSql must be SafeSqlFragment, not a plain string. Use safeSql`...`, ident(), literal(), or keyword() to construct it safely.',
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const services = ESLintUtils.getParserServices(context)
|
||||
const checker = services.program.getTypeChecker()
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const callee = node.callee
|
||||
const isExecuteSql =
|
||||
(callee.type === 'Identifier' && callee.name === 'executeSql') ||
|
||||
(callee.type === 'MemberExpression' &&
|
||||
callee.property.type === 'Identifier' &&
|
||||
callee.property.name === 'executeSql')
|
||||
|
||||
if (!isExecuteSql) return
|
||||
|
||||
const firstArg = node.arguments[0]
|
||||
if (!firstArg || firstArg.type !== 'ObjectExpression') return
|
||||
|
||||
const sqlProp = firstArg.properties.find(
|
||||
(prop) =>
|
||||
prop.type === 'Property' && prop.key.type === 'Identifier' && prop.key.name === 'sql'
|
||||
)
|
||||
if (!sqlProp || sqlProp.type !== 'Property') return
|
||||
|
||||
const tsNode = services.esTreeNodeToTSNodeMap.get(sqlProp.value)
|
||||
const type = checker.getTypeAtLocation(tsNode)
|
||||
const hasBrand = checker.getPropertyOfType(type, '__safeSqlFragmentBrand') !== undefined
|
||||
|
||||
if (!hasBrand) {
|
||||
context.report({ node: sqlProp.value, messageId: 'requireSafeSqlFragment' })
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Separate ESLint config for rules that require TypeScript type information.
|
||||
*
|
||||
* Run via: pnpm lint:type-checks
|
||||
*
|
||||
* Kept separate from eslint.config.cjs because loading a full TypeScript program
|
||||
* (project: true) is memory-intensive and would slow down or OOM the main lint run.
|
||||
*/
|
||||
|
||||
const { defineConfig } = require('eslint/config')
|
||||
const tsparser = require('@typescript-eslint/parser')
|
||||
const requireSafeSqlFragment = require('./eslint-rules/require-safe-sql-fragment.cjs')
|
||||
|
||||
const studioPlugin = {
|
||||
rules: {
|
||||
'require-safe-sql-fragment': requireSafeSqlFragment,
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = defineConfig([
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
studio: studioPlugin,
|
||||
},
|
||||
rules: {
|
||||
'studio/require-safe-sql-fragment': 'warn',
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -11,7 +11,6 @@
|
||||
"start": "next start -p 8082",
|
||||
"lint": "eslint .",
|
||||
"lint:ratchet": "tsx scripts/ratchet-eslint-rules.ts --rule react-hooks/exhaustive-deps --rule import/no-anonymous-default-export --rule @tanstack/query/exhaustive-deps --rule @typescript-eslint/no-explicit-any --rule no-restricted-imports --rule no-restricted-exports --rule react/no-unstable-nested-components",
|
||||
"lint:ratchet:type-checks": "tsx scripts/ratchet-eslint-rules.ts --rule studio/require-safe-sql-fragment --eslint-args \"--config eslint.type-checks.config.cjs .\"",
|
||||
"clean": "rimraf node_modules tsconfig.tsbuildinfo .next .turbo",
|
||||
"test": "vitest --run --coverage",
|
||||
"test:watch": "vitest watch",
|
||||
@@ -176,7 +175,6 @@
|
||||
"@types/recharts": "^1.8.23",
|
||||
"@types/sqlstring": "^2.3.0",
|
||||
"@types/zxcvbn": "^4.4.1",
|
||||
"@typescript-eslint/utils": "8.48.0",
|
||||
"@vitejs/plugin-react": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"@vitest/ui": "catalog:",
|
||||
|
||||
Generated
-3
@@ -1284,9 +1284,6 @@ importers:
|
||||
'@types/zxcvbn':
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.2
|
||||
'@typescript-eslint/utils':
|
||||
specifier: 8.48.0
|
||||
version: 8.48.0(eslint@9.37.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.2)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: 'catalog:'
|
||||
version: 6.0.1(vite@8.0.8(@types/node@22.13.14)(esbuild@0.25.2)(jiti@2.6.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.3))
|
||||
|
||||
Reference in new issue
Block a user