@nitra/cursor 12.9.0 → 12.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-template/settings.template.json +1 -23
- package/CHANGELOG.md +6 -0
- package/bin/n-cursor.js +8 -43
- package/docs/stryker.config.md +0 -2
- package/lib/docs/llm.md +16 -21
- package/lib/docs/omlx.md +20 -25
- package/lib/llm.mjs +25 -7
- package/lib/omlx.mjs +10 -3
- package/package.json +1 -1
- package/rules/abie/docs/main.md +0 -2
- package/rules/abie/lib/docs/http-route.md +0 -2
- package/rules/abie/main.mdc +0 -22
- package/rules/adr/docs/main.md +0 -2
- package/rules/adr/main.mdc +0 -9
- package/rules/bun/docs/main.md +0 -2
- package/rules/bun/main.mdc +1 -15
- package/rules/capacitor/docs/main.md +0 -2
- package/rules/capacitor/main.mdc +0 -6
- package/rules/changelog/docs/main.md +0 -2
- package/rules/changelog/js/agent-workflow.mdc +1 -1
- package/rules/changelog/js/consistency.mjs +3 -3
- package/rules/changelog/js/docs/consistency.md +18 -23
- package/rules/changelog/main.mdc +0 -5
- package/rules/ci4/docs/main.md +0 -2
- package/rules/ci4/main.mdc +0 -5
- package/rules/doc-files/docs/main.md +0 -2
- package/rules/doc-files/js/docs/docgen-crc.md +0 -2
- package/rules/doc-files/js/docs/docgen-extract.md +0 -2
- package/rules/doc-files/js/docs/docgen-files-batch.md +0 -2
- package/rules/doc-files/js/docs/docgen-gen.md +0 -2
- package/rules/doc-files/js/docs/docgen-judge-measure.md +0 -2
- package/rules/doc-files/js/docs/docgen-judge.md +0 -2
- package/rules/doc-files/js/docs/docgen-scan.md +0 -2
- package/rules/doc-files/js/docs/run-lint.md +0 -2
- package/rules/docker/docs/main.md +0 -2
- package/rules/docker/js/docs/lint.md +0 -2
- package/rules/docker/lib/docs/docker-hadolint.md +0 -2
- package/rules/docker/main.mdc +1 -21
- package/rules/efes/docs/main.md +0 -2
- package/rules/efes/main.mdc +0 -1
- package/rules/feedback/docs/main.md +0 -2
- package/rules/ga/docs/main.md +0 -2
- package/rules/ga/js/docs/index.md +0 -1
- package/rules/ga/main.mdc +1 -31
- package/rules/graphql/docs/main.md +0 -2
- package/rules/graphql/main.mdc +0 -5
- package/rules/hasura/docs/main.md +0 -2
- package/rules/hasura/js/docs/index.md +3 -3
- package/rules/hasura/js/docs/migrations.md +0 -2
- package/rules/hasura/main.mdc +1 -11
- package/rules/image-avif/docs/main.md +0 -2
- package/rules/image-avif/main.mdc +1 -9
- package/rules/image-compress/docs/main.md +0 -2
- package/rules/image-compress/js/docs/index.md +0 -1
- package/rules/image-compress/main.mdc +1 -9
- package/rules/js/docs/main.md +0 -2
- package/rules/js/js/dep-policy.mjs +8 -4
- package/rules/js/js/docs/check.md +0 -2
- package/rules/js/js/docs/dep-policy.md +10 -12
- package/rules/js/js/docs/index.md +5 -5
- package/rules/js/js/docs/tooling.md +0 -2
- package/rules/js/js/docs/utils_imports.md +0 -2
- package/rules/js/main.mdc +0 -31
- package/rules/js-bun-db/docs/main.md +0 -2
- package/rules/js-bun-db/js/docs/safety.md +18 -23
- package/rules/js-bun-db/js/safety.mjs +31 -3
- package/rules/js-bun-db/lib/bun-sql-scan.mjs +123 -0
- package/rules/js-bun-db/lib/docs/bun-sql-scan.md +37 -331
- package/rules/js-bun-db/main.mdc +1 -23
- package/rules/js-bun-redis/docs/main.md +0 -2
- package/rules/js-bun-redis/main.mdc +0 -5
- package/rules/js-mssql/docs/main.md +0 -2
- package/rules/js-mssql/main.mdc +0 -12
- package/rules/js-run/docs/main.md +0 -2
- package/rules/js-run/js/docs/runtime.md +15 -13
- package/rules/js-run/js/runtime.mjs +48 -4
- package/rules/js-run/main.mdc +0 -25
- package/rules/k8s/docs/main.md +0 -2
- package/rules/k8s/main.mdc +0 -45
- package/rules/nginx-default-tpl/docs/main.md +0 -2
- package/rules/nginx-default-tpl/main.mdc +0 -13
- package/rules/npm-module/docs/main.md +0 -2
- package/rules/npm-module/js/docs/header_doc_pointer.md +0 -2
- package/rules/npm-module/js/docs/rule_meta.md +0 -2
- package/rules/npm-module/js/docs/skill_meta.md +0 -2
- package/rules/npm-module/main.mdc +1 -15
- package/rules/php/docs/main.md +0 -2
- package/rules/php/js/docs/index.md +0 -1
- package/rules/php/main.mdc +1 -9
- package/rules/python/docs/main.md +0 -2
- package/rules/python/js/docs/index.md +0 -1
- package/rules/python/main.mdc +1 -13
- package/rules/rego/docs/main.md +0 -2
- package/rules/rego/js/docs/index.md +2 -2
- package/rules/rego/js/docs/tooling.md +0 -2
- package/rules/rego/js/tooling.mdc +14 -0
- package/rules/rego/main.mdc +0 -9
- package/rules/rego/policy/package_json/package_json.mdc +12 -0
- package/rules/release/docs/main.md +0 -2
- package/rules/release/main.mdc +2 -2
- package/rules/rust/docs/main.md +0 -2
- package/rules/rust/js/docs/index.md +0 -1
- package/rules/rust/main.mdc +1 -11
- package/rules/rust/policy/package_json/package_json.mdc +12 -0
- package/rules/security/docs/main.md +0 -2
- package/rules/security/js/docs/index.md +0 -1
- package/rules/security/main.mdc +0 -13
- package/rules/style/docs/main.md +0 -2
- package/rules/style/js/docs/index.md +2 -2
- package/rules/style/js/docs/tooling.md +0 -2
- package/rules/style/main.mdc +1 -23
- package/rules/tauri/docs/main.md +0 -2
- package/rules/tauri/main.mdc +1 -11
- package/rules/test/docs/main.md +0 -2
- package/rules/test/js/docs/no-console-store-restore.md +0 -2
- package/rules/test/js/docs/sandbox-aware-test.md +0 -2
- package/rules/test/js/docs/stryker_config.md +0 -2
- package/rules/test/js/docs/vitest-config-pool-forks.md +0 -2
- package/rules/test/main.mdc +1 -21
- package/rules/text/docs/main.md +0 -2
- package/rules/text/js/docs/cspell-fix.md +0 -2
- package/rules/text/js/docs/run-dotenv-linter.md +0 -2
- package/rules/text/js/docs/run-shellcheck.md +0 -2
- package/rules/text/js/docs/run-v8r.md +0 -2
- package/rules/text/main.mdc +0 -33
- package/rules/tool-surface/docs/main.md +0 -2
- package/rules/vue/docs/main.md +0 -2
- package/rules/vue/main.mdc +0 -22
- package/rules/worktree/docs/main.md +0 -2
- package/scripts/docs/auto-rules.md +0 -2
- package/scripts/docs/auto-skills.md +0 -2
- package/scripts/docs/hook.md +13 -12
- package/scripts/docs/post-tool-use-check.md +0 -2
- package/scripts/docs/sync-claude-config.md +1 -3
- package/scripts/docs/sync-setup-bun-deps-action.md +0 -2
- package/scripts/hook.mjs +3 -4
- package/scripts/lib/docs/check-mdc-template-refs.md +0 -2
- package/scripts/lib/docs/index.md +35 -35
- package/scripts/lib/docs/inline-template-links.md +6 -8
- package/scripts/lib/docs/list-project-rules-mdc.md +0 -2
- package/scripts/lib/docs/list-rule-ids.md +0 -2
- package/scripts/lib/docs/mirror-parity.md +8 -10
- package/scripts/lib/docs/read-n-cursor-config-lite.md +0 -2
- package/scripts/lib/docs/rule-meta.md +0 -2
- package/scripts/lib/docs/run-lint.md +0 -2
- package/scripts/lib/docs/run-rule-cli.md +0 -2
- package/scripts/lib/docs/run-rule.md +0 -2
- package/scripts/lib/docs/run-standard-lint.md +0 -2
- package/scripts/lib/docs/run-standard-rule.md +0 -2
- package/scripts/lib/docs/skill-meta.md +0 -2
- package/scripts/lib/docs/timing-summary.md +0 -2
- package/scripts/lib/docs/worktree-notice.md +0 -2
- package/scripts/lib/fix/docs/analyze-escalation.md +0 -2
- package/scripts/lib/fix/docs/index.md +10 -9
- package/scripts/lib/fix/docs/llm-worker.md +18 -8
- package/scripts/lib/fix/docs/orchestrator.md +44 -20
- package/scripts/lib/fix/docs/run-conformance-check.md +0 -2
- package/scripts/lib/fix/docs/t0.md +0 -2
- package/scripts/lib/fix/docs/verbose-block.md +27 -0
- package/scripts/lib/fix/llm-worker.mjs +75 -22
- package/scripts/lib/fix/orchestrator.mjs +13 -3
- package/scripts/lib/fix/verbose-block.mjs +82 -0
- package/scripts/lib/inline-template-links.mjs +32 -22
- package/scripts/lib/mirror-parity.mjs +2 -2
- package/scripts/sync-claude-config.mjs +7 -4
- package/scripts/utils/docs/resolve-js-root.md +0 -2
- package/skills/doc-files/SKILL.md +9 -24
- package/skills/llm-patch/SKILL.md +4 -4
|
@@ -3,355 +3,61 @@ type: JS Module
|
|
|
3
3
|
title: bun-sql-scan.mjs
|
|
4
4
|
resource: npm/rules/js-bun-db/lib/bun-sql-scan.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 76151470
|
|
7
|
+
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
+
score: 100
|
|
7
9
|
---
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
AST-сканер виявляє небезпечні патерни, пов'язані з використанням Bun SQL та PostgreSQL. Він перевіряє, чи не створюється пул з'єднань через `new SQL` всередині функції, що порушує принцип singleton на рівні модуля. Сканер також виявляє виклики `<obj>.unsafe` без відповідного маркера-коментаря `// allow-unsafe: <reason>`, що заборонено за замовчуванням для `sql.unsafe`. Крім того, він ідентифікує динамічні SQL-списки у tagged template `sql\`... IN (${arr.join}) ...\``, які не використовують параметризовані значення, а також шукає інші аномалії у використанні бібліотек `pg` (js-bun-db.mdc).
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Поведінка
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
findPgFormatShimDefinitionInText знаходить визначення pg-format-сумісних шимів у коді, що використовує Bun SQL.
|
|
16
|
+
findPgFormatLikeQueryWrapperInText знаходить pg-сумісні обгортки запитів виду `{ query { ... unsafe ... } }` у коді, що використовує Bun SQL.
|
|
17
|
+
findBunSqlPerRequestConnectionInText знаходить створення нового екземпляра `SQL` всередині функцій, що вказує на неоптимальне використання пулу.
|
|
18
|
+
findBunSqlUnsafeUseWithoutAllowMarkerInText знаходить виклики `<obj>.unsafe` без відповідного маркера-коментаря.
|
|
19
|
+
findBunSqlUnsafeWithInterpolatedTemplateInText знаходить виклики `<obj>.unsafe` з інтерпольованим шаблоном, що створює структурну вразливість.
|
|
20
|
+
findBunSqlPgLeftoverCallInText знаходить виклики `<obj>.connect` або `<obj>.end` у файлах з Bun SQL без маркера-коментаря.
|
|
21
|
+
findUnsafeBunSqlDynamicSqlListInText знаходить динамічні SQL-списки у tagged template, де використовується `.join` замість параметризації.
|
|
22
|
+
findUnsafeBunSqlInListMissingEmptyGuardInText знаходить підстановки списків у `IN (...)`, де відсутня перевірка на пустоту з `throw`.
|
|
23
|
+
textHasBunSqlImport визначає, чи містить текст джерела імпорт імені `sql` або `SQL` з `"bun"`.
|
|
24
|
+
textHasPgLibImport визначає, чи імпортує файл npm-пакет `pg` за допомогою `import` або `require`.
|
|
25
|
+
findPgLibImportInText знаходить конкретні місця імпорту пакета `pg` у коді.
|
|
26
|
+
findPgListenNotifyUsageInText знаходить використання PostgreSQL LISTEN/NOTIFY у коді, що вимагає клієнта `pg`.
|
|
27
|
+
isBunSqlScanSourceFile визначає, чи підходить файл за розширенням для AST-скану.
|
|
28
|
+
findJsonStringifyBeforeJsonbInText знаходить виклики `JSON.stringify` перед використанням `::jsonb` у SQL template literal-ах, що призводить до подвійної серіалізації.
|
|
29
|
+
findSqlArrayWithoutTypeArgInText знаходить виклики `sql.array` або аналогічні, де відсутній другий аргумент (тип).
|
|
14
30
|
|
|
15
|
-
|
|
16
|
-
- **Контракт обробки помилок**: якщо файл не парситься (`parseProgramOrNull` повертає `null`), сканер повертає порожній масив. Це навмисний відмовостійкий шлях: спершу треба полагодити синтаксис, потім перезапускати перевірку.
|
|
17
|
-
- **Opt-in маркери-коментарі**: деякі небезпечні патерни можна свідомо дозволити, проставивши коментар `// allow-unsafe: <reason>` або `// allow-pg-leftover: <reason>` на тому ж рядку, що й виклик, або на рядку безпосередньо вище. Причина (`<reason>`) обов'язкова — інакше маркер не приймається.
|
|
18
|
-
- **Скоп файлу**: pg-format-шими, pg-leftover-виклики та інші «недомігровані» патерни шукаються лише у файлах, де **в цьому ж файлі** є `import { sql|SQL } from 'bun'` — щоб не плутати з невинними збігами імен у непов'язаному коді.
|
|
31
|
+
## Публічний API
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
findPgFormatShimDefinitionInText — Виявляє визначення pg-format-сумісних шимів. Прапорує функції з іменами, що вказують на форматування (`format`, `pgFormat`, `sqlFormat`, `pgFmt`), якщо їхній вміст містить літерали або регулярні вирази з `%L`, `%I`, `%s`. Також виявляє pg-format-специфічні API (`quoteLiteral`, `quoteIdent`, `escapeLiteral`, `escapeIdent`), які не потрібні при використанні Bun SQL.
|
|
21
34
|
|
|
22
|
-
|
|
35
|
+
findPgFormatLikeQueryWrapperInText — Виявляє pg-сумісні обгортки запитів, які маскують виклик `unsafe` під безпечне ім'я, повертаючи потенційну точку для SQL-ін'єкції.
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
findBunSqlPerRequestConnectionInText — Виявляє створення нового об'єкта SQL (`new SQL`) всередині функцій-обробників запитів, замість використання єдиного екземпляра.
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
| ----------------------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
-
| `findBunSqlPerRequestConnectionInText(content, virtualPath?)` | `(string, string?) => { line, snippet }[]` | `new SQL(...)` усередині будь-якої функції (порушення singleton-пулу). |
|
|
29
|
-
| `findBunSqlUnsafeUseWithoutAllowMarkerInText(content, virtualPath?)` | `(string, string?) => { line, snippet }[]` | `<obj>.unsafe(...)` без `// allow-unsafe: <reason>`. |
|
|
30
|
-
| `findBunSqlUnsafeWithInterpolatedTemplateInText(content, virtualPath?)` | `(string, string?) => { line, snippet }[]` | `<obj>.unsafe(\`...${x}...\`)`— interpolated template literal як аргумент`unsafe` (injection-поверхня навіть із allow-маркером). |
|
|
31
|
-
| `findBunSqlPgLeftoverCallInText(content, virtualPath?)` | `(string, string?) => { line, snippet, methodName }[]` | `<obj>.connect(...)` / `<obj>.end(...)` у Bun SQL-файлах без `// allow-pg-leftover: <reason>`. |
|
|
32
|
-
| `findUnsafeBunSqlDynamicSqlListInText(content, virtualPath?)` | `(string, string?) => { line, snippet }[]` | `IN (...)` / `VALUES (...)` з `.join(',')` у template literal. |
|
|
33
|
-
| `findUnsafeBunSqlInListMissingEmptyGuardInText(content, virtualPath?)` | `(string, string?) => { line, snippet, reason, name? }[]` | `IN (${...})` без винесення у змінну або без guard `if (empty) throw` перед запитом. |
|
|
34
|
-
| `findPgFormatShimDefinitionInText(content, virtualPath?)` | `(string, string?) => { line, snippet, kind, name }[]` | Визначення pg-format-сумісних шимів (`format`, `pgFormat`, `quoteLiteral`, ...). |
|
|
35
|
-
| `findPgFormatLikeQueryWrapperInText(content, virtualPath?)` | `(string, string?) => { line, snippet }[]` | Об'єктні pg-сумісні `{ query(text, params) { ... unsafe ... } }`-обгортки. |
|
|
36
|
-
| `findPgLibImportInText(content, virtualPath?)` | `(string, string?) => { line, snippet }[]` | Імпорт/`require('pg')` (точно пакет `pg`, не `pg-format`/`pg-pool`). |
|
|
37
|
-
| `findPgListenNotifyUsageInText(content, virtualPath?)` | `(string, string?) => { line, snippet, kind }[]` | LISTEN/UNLISTEN/NOTIFY-запити та `.on('notification', ...)`-listener'и. |
|
|
39
|
+
findBunSqlUnsafeUseWithoutAllowMarkerInText — Виявляє виклики `unsafe` без відповідного маркера-коментаря. Забороняє використання `unsafe`, якщо значення не контролюється кодом (не вхідні дані користувача) і не є необхідним для підстановки назв таблиць/колонок або динамічного SQL/DDL.
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
findBunSqlUnsafeWithInterpolatedTemplateInText — Виявляє використання `unsafe` з шаблонним літералом, що містить інтерполяцію (`${name}`). Попереджає, що шаблонна підстановка не екранує ідентифікатори та не прив'язує значення, створюючи ризик структурної ін'єкції.
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
| ------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
43
|
-
| `textHasBunSqlImport(content)` | `(string) => boolean` | Текстова перевірка: чи є у файлі `import { sql\|SQL } from 'bun'`. Дешевий pre-filter (без AST). |
|
|
44
|
-
| `textHasPgLibImport(content)` | `(string) => boolean` | Текстова перевірка: чи є у файлі `import ... from 'pg'` або `require('pg')`. Не матчить `pg-format`, `pg-pool`. |
|
|
45
|
-
| `isBunSqlScanSourceFile(relativePathPosix)` | `(string) => boolean` | Чи сканувати цей файл за розширенням: JS/TS-сімʼя (`.js`, `.cjs`, `.mjs`, `.jsx`, `.ts`, `.cts`, `.mts`, `.tsx`), без `.d.ts`. |
|
|
43
|
+
findBunSqlPgLeftoverCallInText — Виявляє виклики `connect` або `end` для pg-клієнта у файлах, що імпортують Bun SQL, без відповідного маркера-коментаря.
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
findUnsafeBunSqlDynamicSqlListInText — Виявляє динамічні SQL-списки у контекстах `IN (...)` або `VALUES (...)`, де використовується метод `.join` серед виразів.
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
findUnsafeBunSqlInListMissingEmptyGuardInText — Виявляє підстановки списків у `IN (...)`, які або не винесені в окрему змінну, або винесені, але перед запитом відсутня перевірка на порожній список.
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
- `BUN_SQL_IMPORT_RE` — `import { sql\|SQL } from 'bun'`.
|
|
53
|
-
- `PG_LIB_IMPORT_RE` — `import ... from 'pg'` або `require('pg')` (без `pg-format`/`pg-pool`).
|
|
54
|
-
- `PG_LISTEN_NOTIFY_SQL_RE` — рядок, що починається з `LISTEN\|UNLISTEN\|NOTIFY` (case-insensitive).
|
|
55
|
-
- `IN_PLACEHOLDER_END_RE` — quasi, що закінчується на `IN ` або `IN (`.
|
|
56
|
-
- `ALLOW_UNSAFE_MARKER_RE` — `allow-unsafe: <непорожня причина>`.
|
|
57
|
-
- `ALLOW_PG_LEFTOVER_MARKER_RE` — `allow-pg-leftover: <непорожня причина>`.
|
|
58
|
-
- `PG_FORMAT_PLACEHOLDER_RE` — `%L`, `%I`, `%s`.
|
|
59
|
-
- `PG_QUERY_FIRST_PARAM_RE` — `text\|sql\|query` (імена першого параметра pg-style query-обгортки).
|
|
49
|
+
textHasBunSqlImport — Перевіряє, чи містить текст джерела імпорт імені `sql` або `SQL` з модуля `"bun"`.
|
|
60
50
|
|
|
61
|
-
|
|
51
|
+
textHasPgLibImport — Перевіряє, чи імпортує файл бібліотеку `pg` (саме клієнт, а не `pg-format` чи `@types/pg`).
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
- `PG_FORMAT_SHIM_FUNC_NAMES` — `{ 'format', 'pgFormat', 'sqlFormat', 'pgFmt' }`.
|
|
65
|
-
- `QUOTE_HELPER_NAMES` — `{ 'quoteLiteral', 'quoteIdent', 'escapeLiteral', 'escapeIdent' }`.
|
|
53
|
+
findPgLibImportInText — Знаходить явний імпорт пакета `pg` у коді, повертаючи місце виявлення для точного відображення у повідомленнях про помилку.
|
|
66
54
|
|
|
67
|
-
|
|
55
|
+
findPgListenNotifyUsageInText — Виявляє використання PostgreSQL команд `LISTEN`/`NOTIFY` у запитах або обробниках подій, оскільки Bun SQL поки не підтримує ці функції.
|
|
68
56
|
|
|
69
|
-
|
|
57
|
+
findJsonStringifyBeforeJsonbInText — Знаходить виклики `JSON.stringify::jsonb` у SQL-шаблонах, що призводить до подвійної серіалізації даних.
|
|
70
58
|
|
|
71
|
-
|
|
59
|
+
findSqlArrayWithoutTypeArgInText — Знаходить виклики функції для створення масиву (`sql.array` тощо) без вказання обов'язкового другого аргументу (типу pg-елемента), що може спричинити невідповідність типів.
|
|
72
60
|
|
|
73
|
-
|
|
61
|
+
## Гарантії поведінки
|
|
74
62
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- **Параметри**: `content` — вихідний код; `virtualPath` — шлях для вибору `lang` парсером.
|
|
78
|
-
- **Повертає**: `Array<{ line: number, snippet: string }>`.
|
|
79
|
-
- **Що робить**: парсить програму через `parseProgramOrNull`. Якщо парс невдалий — повертає `[]`. Інакше обходить AST через `walkAstWithAncestors`. Для кожного `NewExpression` із callee-Identifier `SQL` перевіряє, чи знаходиться вузол усередині функції (через `ancestors.some(isFunctionNode)`). Якщо так — додає `{ line, snippet }` у результат.
|
|
80
|
-
- **Side effects**: жодних (чиста функція, повертає новий масив).
|
|
81
|
-
- **Контракт парсера**: ця функція **не** перевіряє, чи є у файлі `import { sql\|SQL } from 'bun'` — її викликає правило, яке вже встановило цю передумову.
|
|
82
|
-
|
|
83
|
-
#### `findBunSqlUnsafeUseWithoutAllowMarkerInText(content, virtualPath = 'scan.ts')`
|
|
84
|
-
|
|
85
|
-
- **Параметри**: вихідний код і опційний `virtualPath`.
|
|
86
|
-
- **Повертає**: `Array<{ line: number, snippet: string }>`.
|
|
87
|
-
- **Що робить**: парсить програму **разом із коментарями** через `parseProgramAndCommentsOrNull`. Для кожного виклику `<obj>.unsafe(...)` (визначається `isUnsafeCall`) перевіряє наявність маркера `// allow-unsafe: <reason>` поруч (через `hasMarkerCommentNear` + `ALLOW_UNSAFE_MARKER_RE`). Якщо маркера немає — додає у результат.
|
|
88
|
-
- **Семантика**: `<obj>` — будь-який об'єкт (`sql.unsafe`, `tx.unsafe`, `db.unsafe` тощо). Розрізняти імена не треба, бо файл сканується лише при наявності Bun SQL-імпорту (це передумова правила; у самій функції не перевіряється).
|
|
89
|
-
- **Side effects**: немає.
|
|
90
|
-
|
|
91
|
-
#### `findBunSqlUnsafeWithInterpolatedTemplateInText(content, virtualPath = 'scan.ts')`
|
|
92
|
-
|
|
93
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
94
|
-
- **Повертає**: `Array<{ line: number, snippet: string }>`.
|
|
95
|
-
- **Що робить**: знаходить виклики `<obj>.unsafe(...)`, де перший аргумент — `TemplateLiteral` із непорожнім масивом `expressions`. Тобто `sql.unsafe(\`... ${x} ...\`)`. Цей патерн прапорує **навіть з маркером `// allow-unsafe`**, бо template-interpolation у `unsafe` — структурна injection-поверхня (не екранує identifier'ів і не біндить значення).
|
|
96
|
-
- **Що НЕ прапорує**: статичні рядки (`Literal`, `StringLiteral`, `TemplateLiteral` без `${...}`), виклики з готовим текстом-змінною. Для них діє основна перевірка `findBunSqlUnsafeUseWithoutAllowMarkerInText`.
|
|
97
|
-
- **Side effects**: немає.
|
|
98
|
-
|
|
99
|
-
#### `findBunSqlPgLeftoverCallInText(content, virtualPath = 'scan.ts')`
|
|
100
|
-
|
|
101
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
102
|
-
- **Повертає**: `Array<{ line: number, snippet: string, methodName: 'connect' \| 'end' }>`.
|
|
103
|
-
- **Що робить**: спочатку перевіряє через `textHasBunSqlImport(content)` — якщо у файлі **немає** імпорту Bun SQL, повертає `[]` (скоп навмисно вузький: метод-імена `connect`/`end` занадто загальні — є у WebSocket, Stream, інших API). Інакше парсить програму з коментарями. Для кожного `<obj>.connect(...)` / `<obj>.end(...)` (через `asPgLeftoverCall`) перевіряє маркер `// allow-pg-leftover: <reason>`. Якщо маркера немає — додає у результат із `methodName`.
|
|
104
|
-
- **Side effects**: немає.
|
|
105
|
-
|
|
106
|
-
#### `findUnsafeBunSqlDynamicSqlListInText(content, virtualPath = 'scan.ts')`
|
|
107
|
-
|
|
108
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
109
|
-
- **Повертає**: `Array<{ line: number, snippet: string }>`.
|
|
110
|
-
- **Що робить**: знаходить `TemplateLiteral` (як standalone, так і всередині `TaggedTemplateExpression`), де контекст — `IN (...)` / `VALUES (...)` (`isSqlListContextTemplate`) і серед expressions є виклик `.join(...)` (`isJoinCall`). Це означає, що у запит потрапляє конкатенований SQL замість параметризованих значень — треба використати `sql([...])`-helper.
|
|
111
|
-
- **Side effects**: немає.
|
|
112
|
-
|
|
113
|
-
#### `findUnsafeBunSqlInListMissingEmptyGuardInText(content, virtualPath = 'scan.ts')`
|
|
114
|
-
|
|
115
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
116
|
-
- **Повертає**: `Array<{ line: number, snippet: string, reason: 'not_var' \| 'sql_helper_not_var' \| 'missing_guard', name? }>`.
|
|
117
|
-
- **Що робить**: знаходить SQL-list контексти (`IN (`/`IN`/`VALUES`-quasi) і обробляє кожен такий template через `collectInListGuardViolationsFromTemplate`. Якщо expression у `${...}` після `IN ` / `IN (` — це не Identifier (наприклад, виклик або складний вираз) — повертає `reason: 'not_var'`. Якщо це `sql(<non-Identifier>)` — `reason: 'sql_helper_not_var'`. Якщо це Identifier, але перед запитом у поточному `BlockStatement` немає guard'а `if (!ids.length) throw ...` / `if (ids.length === 0) throw ...` — `reason: 'missing_guard'` із `name`.
|
|
118
|
-
- **Side effects**: немає.
|
|
119
|
-
|
|
120
|
-
#### `findPgFormatShimDefinitionInText(content, virtualPath = 'scan.ts')`
|
|
121
|
-
|
|
122
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
123
|
-
- **Повертає**: `Array<{ line: number, snippet: string, kind: 'format_function' \| 'quote_helper', name: string }>`.
|
|
124
|
-
- **Що робить**: pre-filter: `textHasBunSqlImport(content)` — якщо у файлі немає Bun SQL, повертає `[]`. Інакше парсить програму та шукає визначення функцій верхнього рівня (`FunctionDeclaration` або `VariableDeclarator` з `ArrowFunctionExpression`/`FunctionExpression`). Для кожного:
|
|
125
|
-
- якщо ім'я ∈ `QUOTE_HELPER_NAMES` (`quoteLiteral`, `quoteIdent`, `escapeLiteral`, `escapeIdent`) — прапорує **незалежно від тіла** як `'quote_helper'`;
|
|
126
|
-
- якщо ім'я ∈ `PG_FORMAT_SHIM_FUNC_NAMES` (`format`, `pgFormat`, `sqlFormat`, `pgFmt`) **і** тіло містить літерал з `%L\|%I\|%s` (`nodeContainsPgFormatPlaceholder`) — прапорує як `'format_function'`.
|
|
127
|
-
- **Snippet**: обмежений до 240 символів від `node.start`.
|
|
128
|
-
- **Side effects**: немає.
|
|
129
|
-
|
|
130
|
-
#### `findPgFormatLikeQueryWrapperInText(content, virtualPath = 'scan.ts')`
|
|
131
|
-
|
|
132
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
133
|
-
- **Повертає**: `Array<{ line: number, snippet: string }>`.
|
|
134
|
-
- **Що робить**: pre-filter `textHasBunSqlImport`. Далі обходить AST, шукає `ObjectExpression`, у яких є властивість `query` з функцією-значенням, що:
|
|
135
|
-
- має 1–2 параметри (`hasPgQuerySignature`);
|
|
136
|
-
- перший параметр — Identifier з типовим pg-іменем (`text`/`sql`/`query`);
|
|
137
|
-
- у тілі функції є виклик `<obj>.unsafe(...)` (`nodeContainsUnsafeCall`).
|
|
138
|
-
- **Призначення**: знаходить «маскування» `unsafe` під безпечним ім'ям `query(text, params)` у pg-сумісній обгортці.
|
|
139
|
-
- **Side effects**: немає.
|
|
140
|
-
|
|
141
|
-
#### `findPgLibImportInText(content, virtualPath = 'scan.ts')`
|
|
142
|
-
|
|
143
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
144
|
-
- **Повертає**: `Array<{ line: number, snippet: string }>`.
|
|
145
|
-
- **Що робить**: парсить програму, шукає `ImportDeclaration` із `source.value === 'pg'` або `CallExpression` виду `require('pg')`. Точне співпадіння — `pg-format`/`pg-pool`/`@types/pg` не матчаться.
|
|
146
|
-
- **Side effects**: немає.
|
|
147
|
-
|
|
148
|
-
#### `findPgListenNotifyUsageInText(content, virtualPath = 'scan.ts')`
|
|
149
|
-
|
|
150
|
-
- **Параметри**: вихідний код, `virtualPath`.
|
|
151
|
-
- **Повертає**: `Array<{ line: number, snippet: string, kind: 'listen_sql' \| 'notify_sql' \| 'unlisten_sql' \| 'notification_listener' }>`.
|
|
152
|
-
- **Що робить**: обходить AST. Для кожного вузла пробує дві сигнатури:
|
|
153
|
-
- `listenNotifyFromCallExpression`: `<obj>.query(...)` / `.queryArray(...)` / `.queryStream(...)` з першим аргументом — string literal або template literal, що починається з `LISTEN`/`UNLISTEN`/`NOTIFY`; також `<obj>.on('notification', fn)`.
|
|
154
|
-
- `listenNotifyFromTaggedTemplate`: TaggedTemplateExpression, де перший quasi починається з `LISTEN`/`UNLISTEN`/`NOTIFY`.
|
|
155
|
-
- **Призначення**: легітимна потреба у клієнті `pg` — Bun SQL не має LISTEN/NOTIFY. Сигнал для правила «можна виключити pg із заборони у цьому файлі».
|
|
156
|
-
- **Side effects**: немає.
|
|
157
|
-
|
|
158
|
-
### Експортовані предикати
|
|
159
|
-
|
|
160
|
-
#### `textHasBunSqlImport(content)`
|
|
161
|
-
|
|
162
|
-
- Сигнатура: `(string) => boolean`.
|
|
163
|
-
- Текстова перевірка `BUN_SQL_IMPORT_RE`. Без AST — для дешевого pre-filter'у при зборі ознак авто-детекту правил.
|
|
164
|
-
|
|
165
|
-
#### `textHasPgLibImport(content)`
|
|
166
|
-
|
|
167
|
-
- Сигнатура: `(string) => boolean`.
|
|
168
|
-
- Текстова перевірка `PG_LIB_IMPORT_RE`. Швидкий pre-filter; точне місце імпорту — через `findPgLibImportInText`.
|
|
169
|
-
|
|
170
|
-
#### `isBunSqlScanSourceFile(relativePathPosix)`
|
|
171
|
-
|
|
172
|
-
- Сигнатура: `(string) => boolean`.
|
|
173
|
-
- Перевіряє розширення за `SOURCE_FILE_RE` (`.js`, `.cjs`, `.mjs`, `.jsx`, `.ts`, `.cts`, `.mts`, `.tsx`) і відкидає `.d.ts`-файли.
|
|
174
|
-
|
|
175
|
-
### Внутрішні допоміжні функції (не експортуються)
|
|
176
|
-
|
|
177
|
-
| Функція | Призначення |
|
|
178
|
-
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
179
|
-
| `isLengthMember(node, name)` | `MemberExpression` виду `<name>.length`. |
|
|
180
|
-
| `isZeroNumberLiteral(node)` | Числовий літерал `0` (`NumericLiteral` або `Literal`). |
|
|
181
|
-
| `isSqlHelperIdentifier(node)` | Identifier з ім'ям `sql`. |
|
|
182
|
-
| `extractInListVarNameFromExpr(expr)` | З `${ids}` → `{ name: 'ids' }`; з `${sql(ids)}` → `{ name: 'ids' }`; інакше — `{ error: 'not_var' \| 'sql_helper_not_var' }`. |
|
|
183
|
-
| `isEmptyListTest(test, name)` | Чи це `!ids.length` / `ids.length === 0` / `<= 0` / `< 1` (і дзеркальні форми). |
|
|
184
|
-
| `consequentHasThrow(consequent)` | Чи `consequent` (statement або `BlockStatement`) містить `ThrowStatement`. |
|
|
185
|
-
| `hasEmptyGuardBefore(block, statementIndex, name)` | Чи у `block.body` до індексу `statementIndex` є `IfStatement` з `isEmptyListTest` + `consequentHasThrow`. |
|
|
186
|
-
| `findEnclosingBlockAndStatementIndex(ancestors)` | Шукає найближчий `BlockStatement` і індекс statement у ньому. |
|
|
187
|
-
| `isNewSqlConstructor(node)` | `NewExpression` з callee-Identifier `SQL`. |
|
|
188
|
-
| `isUnsafeCall(node)` / `isUnsafeCallNode` (alias) | `CallExpression` з `callee` = `MemberExpression` `<obj>.unsafe` (не computed). |
|
|
189
|
-
| `hasMarkerCommentNear(callNode, comments, content, markerRe)` | Чи є маркер-коментар, що матчиться на `markerRe`, на тому ж рядку, що `callNode.start`, або рядком вище. Block-коментарі: важливим є `endLine`. |
|
|
190
|
-
| `asPgLeftoverCall(node)` | Якщо це `<obj>.connect(...)` або `<obj>.end(...)` — повертає `{ name }`; інакше `null`. |
|
|
191
|
-
| `propertyKeyName(key)` | Витягає ім'я з `Property.key` (`Identifier.name` або `Literal.value` для string/number). |
|
|
192
|
-
| `nodeContainsPgFormatPlaceholder(root)` | Чи у піддереві є літерал/regex/template з `%L`/`%I`/`%s`. |
|
|
193
|
-
| `asNamedFunctionDecl(node)` | З `FunctionDeclaration` або `VariableDeclarator` з функцією-init — повертає `{ name, body }`. |
|
|
194
|
-
| `asPgFormatLikeQueryProp(prop)` | Чи це `{ query(text, params) { ... unsafe ... } }`-Property. |
|
|
195
|
-
| `hasPgQuerySignature(params)` | 1–2 параметри, перший — Identifier з ім'ям `text`/`sql`/`query`. |
|
|
196
|
-
| `nodeContainsUnsafeCall(root)` | Чи у піддереві є `<obj>.unsafe(...)`. |
|
|
197
|
-
| `collectInListGuardViolationsFromTemplate(template, ancestors, content, out)` | Збирає порушення для одного template'а у контексті `IN (...)`. **Side effect**: пушить у переданий буфер `out`. |
|
|
198
|
-
| `getStringLiteralValue(node)` | `Literal`/`StringLiteral`-значення (string) або `null`. |
|
|
199
|
-
| `isRequireOfModule(node, moduleName)` | Чи `CallExpression` — це `require('<moduleName>')` (точне співпадіння). |
|
|
200
|
-
| `listenNotifyFromCallExpression(node)` | Для `<obj>.query/.queryArray/.queryStream(...)` з LISTEN/NOTIFY-рядком — повертає kind; для `<obj>.on('notification', ...)` — `'notification_listener'`. |
|
|
201
|
-
| `listenNotifyFromTaggedTemplate(node)` | Для TaggedTemplateExpression, де перший quasi починається з LISTEN/UNLISTEN/NOTIFY — повертає kind. |
|
|
202
|
-
| `sqlStringStartsWithListenNotify(arg)` | Аналіз першого аргумента `.query(...)`: string literal або template literal → kind. |
|
|
203
|
-
| `kindFromListenNotifyMatch(text)` | Текст → `'listen_sql'`/`'notify_sql'`/`'unlisten_sql'` або `null` (UNLISTEN мапиться у `unlisten_sql`). |
|
|
204
|
-
|
|
205
|
-
## Залежності
|
|
206
|
-
|
|
207
|
-
### Внутрішні (з `../../../scripts/utils/ast-scan-utils.mjs`)
|
|
208
|
-
|
|
209
|
-
Усі named imports:
|
|
210
|
-
|
|
211
|
-
- `isFunctionNode(node)` — чи вузол — функція (FunctionDeclaration / FunctionExpression / ArrowFunctionExpression / MethodDefinition тощо).
|
|
212
|
-
- `isJoinCall(expr)` — чи вираз — виклик `<obj>.join(...)`.
|
|
213
|
-
- `isSqlListContextTemplate(template)` — чи `TemplateLiteral` має `IN (...)` / `VALUES (...)` контекст у quasi.
|
|
214
|
-
- `normalizeSnippet(s)` — нормалізує snippet (стискає пробіли тощо).
|
|
215
|
-
- `offsetToLine(content, offset)` — переводить байтовий offset у номер рядка (1-індекс).
|
|
216
|
-
- `parseProgramAndCommentsOrNull(content, virtualPath)` — парсить через oxc-parser, повертає `{ program, comments }` або `null` при помилці.
|
|
217
|
-
- `parseProgramOrNull(content, virtualPath)` — парсить програму без коментарів, повертає `program` або `null`.
|
|
218
|
-
- `templateQuasisText(template)` — обʼєднує текст quasis у один рядок (без expressions).
|
|
219
|
-
- `walkAstWithAncestors(root, ancestors, visitor)` — обхід AST із передачею стека ancestors у visitor.
|
|
220
|
-
|
|
221
|
-
### Зовнішні
|
|
222
|
-
|
|
223
|
-
Прямих імпортів зовнішніх npm-пакетів немає. Опосередковано через `ast-scan-utils.mjs` використовується **oxc-parser**.
|
|
224
|
-
|
|
225
|
-
### Глобальні / runtime
|
|
226
|
-
|
|
227
|
-
- Стандартні JS-API: `RegExp`, `Set`, `Array`, `String.prototype.slice`/`Math.min`/`Array.prototype.entries`/`Array.prototype.indexOf` тощо.
|
|
228
|
-
|
|
229
|
-
## Потік виконання / Використання
|
|
230
|
-
|
|
231
|
-
### Загальна модель
|
|
232
|
-
|
|
233
|
-
Файл — це **бібліотека функцій без побічних ефектів**. Сам по собі модуль не виконує сканування при імпорті: тільки експортує функції-фабрики результатів. Викликаються вони з check-функцій правил (`check-*.mjs`) у `npm/rules/js-bun-db/`.
|
|
234
|
-
|
|
235
|
-
### Типовий потік виклику з правила
|
|
236
|
-
|
|
237
|
-
1. Правило отримує список файлів проекту.
|
|
238
|
-
2. Для кожного файлу фільтрує через `isBunSqlScanSourceFile(relativePathPosix)` — лише JS/TS-файли, без `.d.ts`.
|
|
239
|
-
3. Для дешевого pre-filter'у читає вміст і викликає `textHasBunSqlImport(content)`. Якщо `false` — пропускає файл (для скан-функцій, що працюють лише у Bun SQL-файлах).
|
|
240
|
-
4. Викликає одну з функцій-сканерів `find...InText(content, relativePath)`. Отримує масив порушень із `line`/`snippet`/інколи додатковими полями (`reason`, `name`, `kind`, `methodName`).
|
|
241
|
-
5. Перетворює порушення на `fail`-повідомлення у форматі правила.
|
|
242
|
-
|
|
243
|
-
### Загальний контракт парсера
|
|
244
|
-
|
|
245
|
-
- Якщо `parseProgramOrNull` / `parseProgramAndCommentsOrNull` повертає `null` (синтаксична помилка) — сканер повертає `[]`. **Це не еквівалентно «жодних порушень»**: правило має іншим механізмом (інші лінтери) переконатися, що файл взагалі парситься.
|
|
246
|
-
- `virtualPath` (за замовчуванням `'scan.ts'`) передається парсеру для вибору мови (`lang` — `ts`/`tsx`/`js`/`jsx`/`cjs`/`mjs` тощо за розширенням). За замовчуванням `'scan.ts'` — щоб TypeScript-конструкції не ламали парс при не-TS-файлах із TS-подібним кодом.
|
|
247
|
-
|
|
248
|
-
### Опт-ін маркери коментарів (формат і правила позиціювання)
|
|
249
|
-
|
|
250
|
-
Дозволені маркери:
|
|
251
|
-
|
|
252
|
-
- `// allow-unsafe: <reason>` — для `<obj>.unsafe(...)`.
|
|
253
|
-
- `// allow-pg-leftover: <reason>` — для `<obj>.connect(...)` / `<obj>.end(...)`.
|
|
254
|
-
|
|
255
|
-
Правила позиціювання (через `hasMarkerCommentNear`):
|
|
256
|
-
|
|
257
|
-
- Маркер дійсний, якщо коментар закінчується на тому ж рядку, що й виклик (`trailing comment`), **або** на рядку, що безпосередньо передує виклику.
|
|
258
|
-
- Між коментарем і викликом **не** допускається порожній рядок (відірваний коментар не зараховується).
|
|
259
|
-
- `<reason>` має бути непорожнім (хоча б один не-пробільний символ після `:`) — інакше маркер не приймається.
|
|
260
|
-
- Допускається як `Line`-коментар (`// ...`), так і `Block`-коментар (`/* ... */`); для блокового важлива саме `endLine` (бо block може займати кілька рядків).
|
|
261
|
-
|
|
262
|
-
### Послідовність ухвалення рішення для деяких сканерів
|
|
263
|
-
|
|
264
|
-
**`findBunSqlUnsafeWithInterpolatedTemplateInText`** працює **незалежно** від `findBunSqlUnsafeUseWithoutAllowMarkerInText` — interpolated template у `unsafe` прапорується навіть із `// allow-unsafe`. Це навмисно: маркер дозволяє статичний `unsafe`-рядок, але не виправдовує template-interpolation.
|
|
265
|
-
|
|
266
|
-
**`findUnsafeBunSqlInListMissingEmptyGuardInText`** і **`findUnsafeBunSqlDynamicSqlListInText`** покривають дотичні, але різні випадки `IN (...)`:
|
|
267
|
-
|
|
268
|
-
- `join`-конкатенація у будь-якому контексті списку (`IN`/`VALUES`) → `findUnsafeBunSqlDynamicSqlListInText`.
|
|
269
|
-
- `IN (${vars})` без guard'у → `findUnsafeBunSqlInListMissingEmptyGuardInText`.
|
|
270
|
-
|
|
271
|
-
### Приклади для розуміння (концептуально)
|
|
272
|
-
|
|
273
|
-
1. **`new SQL(...)` per-request** — прапорує:
|
|
274
|
-
|
|
275
|
-
```js
|
|
276
|
-
import { SQL } from 'bun'
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
*
|
|
280
|
-
*/
|
|
281
|
-
export async function handler(req) {
|
|
282
|
-
const sql = new SQL(process.env.DATABASE_URL) // <-- порушення
|
|
283
|
-
return sql`select 1`
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
2. **`unsafe` без маркера** — прапорує:
|
|
288
|
-
|
|
289
|
-
```js
|
|
290
|
-
import { sql } from 'bun'
|
|
291
|
-
|
|
292
|
-
const text = `select * from ${tableName}`
|
|
293
|
-
sql.unsafe(text) // <-- порушення: немає // allow-unsafe: <reason>
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
3. **`unsafe` з interpolated template** — прапорує навіть з маркером:
|
|
297
|
-
|
|
298
|
-
```js
|
|
299
|
-
sql.unsafe(`select * from ${tableName}`) // allow-unsafe: dynamic-table
|
|
300
|
-
// ^-- прапорується сканером findBunSqlUnsafeWithInterpolatedTemplateInText
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
4. **`IN (${ids})` без guard'у** — прапорує:
|
|
304
|
-
|
|
305
|
-
```js
|
|
306
|
-
const ids = req.body.ids
|
|
307
|
-
await sql`select * from t where id in (${ids})` // <-- reason: 'missing_guard'
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
Виправлення — додати guard:
|
|
311
|
-
|
|
312
|
-
```js
|
|
313
|
-
if (!ids.length) throw new Error('empty ids')
|
|
314
|
-
await sql`select * from t where id in (${sql(ids)})`
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
5. **pg-leftover у Bun SQL-файлі** — прапорує:
|
|
318
|
-
|
|
319
|
-
```js
|
|
320
|
-
import { sql } from 'bun'
|
|
321
|
-
|
|
322
|
-
await sql.end() // <-- порушення: немає // allow-pg-leftover: <reason>
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
### Інваріанти
|
|
326
|
-
|
|
327
|
-
- Усі експортовані функції **детерміновані**: для одного й того ж `content` і `virtualPath` повертають однаковий результат.
|
|
328
|
-
- Функції **не модифікують глобальний стан** і не мають кешу — кешування (якщо потрібне) робиться на рівні викликача (правила).
|
|
329
|
-
- Знахідки повертаються у порядку обходу AST (від `walkAstWithAncestors`); `line` — 1-індексований.
|
|
330
|
-
- Snippet у `findPgFormatShimDefinitionInText` обмежений до 240 символів; у решті — це повний текст знайденого вузла.
|
|
331
|
-
|
|
332
|
-
## Rebuild Test
|
|
333
|
-
|
|
334
|
-
Контрольний перелік, що дає змогу відновити інваріанти та логіку модуля «з нуля» лише з документації:
|
|
335
|
-
|
|
336
|
-
1. **Список експортів** (10 функцій):
|
|
337
|
-
- сканери: `findBunSqlPerRequestConnectionInText`, `findBunSqlUnsafeUseWithoutAllowMarkerInText`, `findBunSqlUnsafeWithInterpolatedTemplateInText`, `findBunSqlPgLeftoverCallInText`, `findUnsafeBunSqlDynamicSqlListInText`, `findUnsafeBunSqlInListMissingEmptyGuardInText`, `findPgFormatShimDefinitionInText`, `findPgFormatLikeQueryWrapperInText`, `findPgLibImportInText`, `findPgListenNotifyUsageInText`;
|
|
338
|
-
- предикати: `textHasBunSqlImport`, `textHasPgLibImport`, `isBunSqlScanSourceFile`.
|
|
339
|
-
2. **Контракт парс-помилок**: будь-який сканер, що отримав `null` від `parseProgramOrNull`/`parseProgramAndCommentsOrNull`, повертає `[]`.
|
|
340
|
-
3. **Опт-ін маркери**: `// allow-unsafe: <reason>` (для `unsafe`), `// allow-pg-leftover: <reason>` (для `connect`/`end`). Дозволені позиції — той самий рядок (trailing) або безпосередньо попередній рядок. Причина обов'язкова.
|
|
341
|
-
4. **Скоп Bun SQL-залежних сканерів**: `findBunSqlPgLeftoverCallInText`, `findPgFormatShimDefinitionInText`, `findPgFormatLikeQueryWrapperInText` спочатку перевіряють `textHasBunSqlImport(content)` і повертають `[]`, якщо `false`.
|
|
342
|
-
5. **Структура повернень**: усі сканери повертають масиви об'єктів із як мінімум `{ line: number, snippet: string }`; деякі додають `reason` / `name` / `kind` / `methodName`.
|
|
343
|
-
6. **AST-семантика, не regex**: усі сканери використовують `walkAstWithAncestors` і AST-предикати; regex використовуються лише для текстових pre-filter'ів і коротких рядкових патернів.
|
|
344
|
-
7. **Розширення для скану**: `.js`, `.cjs`, `.mjs`, `.jsx`, `.ts`, `.cts`, `.mts`, `.tsx`; `.d.ts` — виключено.
|
|
345
|
-
8. **`IN (...)`/`VALUES (...)` guard-логіка**:
|
|
346
|
-
- `not_var` — у `${...}` стоїть не Identifier;
|
|
347
|
-
- `sql_helper_not_var` — у `${sql(<x>)}` `x` — не Identifier;
|
|
348
|
-
- `missing_guard` — Identifier є, але перед запитом у тому ж `BlockStatement` немає `if (!ids.length) throw` / `if (ids.length === 0/<=0/<1) throw` (включно з дзеркальними `0 === ids.length` для `==`/`===`).
|
|
349
|
-
9. **pg-format-шими**: іменам `quoteLiteral`/`quoteIdent`/`escapeLiteral`/`escapeIdent` достатньо самого імені; іменам `format`/`pgFormat`/`sqlFormat`/`pgFmt` — додатково потрібен `%L`/`%I`/`%s` десь у тілі (літерал/regex/template).
|
|
350
|
-
10. **pg-сумісний query-wrapper**: `Property` з `key === 'query'` у `ObjectExpression`, з функцією-значенням, що має 1–2 параметри (перший — Identifier `text`/`sql`/`query`) і викликає `<obj>.unsafe(...)` десь у тілі.
|
|
351
|
-
11. **LISTEN/NOTIFY-сигнали**: `<obj>.query/.queryArray/.queryStream(...)` з першим аргументом — string/template literal, що починається з `LISTEN`/`UNLISTEN`/`NOTIFY` (case-insensitive); `<obj>.on('notification', ...)`; TaggedTemplateExpression з тим самим початком у першому quasi.
|
|
352
|
-
12. **pg-імпорт**: `ImportDeclaration` з `source.value === 'pg'` або `CallExpression` `require('pg')` — точне співпадіння; `pg-format`/`pg-pool` не матчаться.
|
|
353
|
-
13. **`new SQL(...)` per-request**: `NewExpression` із callee-Identifier `SQL` і ancestors, серед яких є функція.
|
|
354
|
-
14. **Snippet limit у `findPgFormatShimDefinitionInText`**: 240 символів від `node.start` (через `Math.min(node.end, node.start + 240)`); решта сканерів використовують `content.slice(start, end)`.
|
|
355
|
-
15. **Determinism і відсутність side effects**: усі експортовані функції — чисті; локальний `collectInListGuardViolationsFromTemplate` має side effect лише на переданий буфер `out`.
|
|
356
|
-
|
|
357
|
-
Якщо всі ці пункти відтворені в коді — сканер семантично еквівалентний оригіналу.
|
|
63
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
package/rules/js-bun-db/main.mdc
CHANGED
|
@@ -5,26 +5,4 @@ alwaysApply: false
|
|
|
5
5
|
version: '1.15'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Правило забезпечує безпечне підключення й виконання запитів через Bun native SQL замість `pg` / `pg-format` / `mysql2`, із захистом від SQL injection, pg-leftover та небезпечних патернів `sql.unsafe`.
|
|
9
|
-
|
|
10
|
-
[js-bun-db-bun-sql-migration](./js/bun-sql-migration.mdc)
|
|
11
|
-
|
|
12
|
-
[js-bun-db-pg-format-shim](./js/pg-format-shim.mdc)
|
|
13
|
-
|
|
14
|
-
[js-bun-db-pg-listen-notify](./js/pg-listen-notify.mdc)
|
|
15
|
-
|
|
16
|
-
[js-bun-db-pg-format-identifiers](./js/pg-format-identifiers.mdc)
|
|
17
|
-
|
|
18
|
-
[js-bun-db-connection](./js/connection.mdc)
|
|
19
|
-
|
|
20
|
-
[js-bun-db-query-safety](./js/query-safety.mdc)
|
|
21
|
-
|
|
22
|
-
[js-bun-db-sql-array](./js/sql-array.mdc)
|
|
23
|
-
|
|
24
|
-
[js-bun-db-unsafe](./js/unsafe.mdc)
|
|
25
|
-
|
|
26
|
-
[js-bun-db-pg-leftover](./js/pg-leftover.mdc)
|
|
27
|
-
|
|
28
|
-
## Швидкий gate через conftest
|
|
29
|
-
|
|
30
|
-
[js-bun-db-package-json](./policy/package_json/package_json.mdc)
|
|
8
|
+
Правило забезпечує безпечне підключення й виконання запитів через Bun native SQL замість `pg` / `pg-format` / `mysql2`, із захистом від SQL injection, pg-leftover та небезпечних патернів `sql.unsafe`.
|
|
@@ -8,8 +8,6 @@ docgen:
|
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Огляд
|
|
12
|
-
|
|
13
11
|
Виконує перевірку, застосовуючи правила, аналізуючи логіку JS-контекстів та перевіряючи політики на основі конфігурацій із meta.json. Збирає посилання MDC. Публічна функція run ініціює повний запуск, що включає завантаження конфігурацій, застосування білих списків та формування зведеного звіту. Операція є Read-only (не пише ФС/БД) та використовує кешування у межах прогону.
|
|
14
12
|
|
|
15
13
|
## Поведінка
|
|
@@ -7,8 +7,3 @@ version: '1.2'
|
|
|
7
7
|
|
|
8
8
|
Правило забороняє використання застарілих redis-клієнтів (`ioredis`, `node-redis`, `redis`, `@redis/*`) і вимагає переходу на **Bun native Redis** (`import { redis } from 'bun'`). Підтримувана версія Redis: **7.2+**.
|
|
9
9
|
|
|
10
|
-
[js-bun-redis-imports](./js/imports.mdc)
|
|
11
|
-
|
|
12
|
-
[js-bun-redis-package_json](./js/package_json.mdc)
|
|
13
|
-
|
|
14
|
-
[js-bun-redis-policy-package_json](./policy/package_json/package_json.mdc)
|
|
@@ -8,8 +8,6 @@ docgen:
|
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Огляд
|
|
12
|
-
|
|
13
11
|
Модуль валідує відповідність даних, використовуючи логіку, визначену у meta.json. Він перевіряє відповідність конфігурацій та білих списків. Публічна функція run ініціює процес. Модуль є Read-only, тобто не виконує запис у файлову систему чи базу даних.
|
|
14
12
|
|
|
15
13
|
## Поведінка
|
package/rules/js-mssql/main.mdc
CHANGED
|
@@ -7,20 +7,8 @@ version: '1.4'
|
|
|
7
7
|
|
|
8
8
|
Правило охоплює безпечне використання пакету `mssql` у Node.js: мінімальну версію залежності, singleton ConnectionPool, заборону небезпечних шаблонів запитів і захист від SQL injection через TVP або числовий парсинг IN-списків.
|
|
9
9
|
|
|
10
|
-
[js-mssql-mssql-version](./js/mssql-version.mdc)
|
|
11
|
-
|
|
12
|
-
[js-mssql-mssql-pool](./js/mssql-pool.mdc)
|
|
13
|
-
|
|
14
|
-
[js-mssql-mssql-query-template](./js/mssql-query-template.mdc)
|
|
15
|
-
|
|
16
|
-
[js-mssql-mssql-tvp](./js/mssql-tvp.mdc)
|
|
17
|
-
|
|
18
|
-
[js-mssql-mssql-in-list](./js/mssql-in-list.mdc)
|
|
19
|
-
|
|
20
10
|
## Швидкий gate через conftest
|
|
21
11
|
|
|
22
12
|
Rego-перевірки (`policy/`) — швидкий синхронний gate для одиничного `package.json` без JS-рантайму:
|
|
23
13
|
|
|
24
|
-
[js-mssql-package_json](./policy/package_json/package_json.mdc)
|
|
25
|
-
|
|
26
14
|
JS-перевірка (`deps.mjs`) — authoritative: охоплює всі `package.json` репо, AST-скан JS/TS-джерел і повний semver-triple-compare. Rego-gate — доповнення, не заміна.
|
|
@@ -8,8 +8,6 @@ docgen:
|
|
|
8
8
|
score: 100
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Огляд
|
|
12
|
-
|
|
13
11
|
Модуль виконує визначене правило. Він ініціює стандартний запуск правила, використовуючи директорію правила та контекст прогону. При запуску як окрема програма, він завантажує конфігурації, на які спирається код, зокрема `meta.json`. Функціональність є read-only, і дані кешуються у межах прогону. Фінальний результат визначається викликом публічної функції `run`.
|
|
14
12
|
|
|
15
13
|
## Поведінка
|
|
@@ -3,30 +3,32 @@ type: JS Module
|
|
|
3
3
|
title: runtime.mjs
|
|
4
4
|
resource: npm/rules/js-run/js/runtime.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
7
|
-
|
|
6
|
+
crc: 5b447a0f
|
|
7
|
+
model: claude-sonnet-4-6
|
|
8
|
+
score: 100
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
check перевіряє правила js-run.mdc лише для workspace-пакетів.
|
|
11
|
+
Модуль перевіряє відповідність правилам `js-run.mdc` для всіх workspace-пакетів монорепо (не кореневого пакета). Frontend-пакети (з `vite` у `devDependencies`) пропускаються повністю — перевіряються лише backend-пакети.
|
|
12
12
|
|
|
13
13
|
## Поведінка
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
Перевіряє відповідність правил нейминга та експортів для файлів усередині `#conn/`
|
|
15
|
+
`check` обходить workspace-пакети і для кожного запускає перевірки:
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
1. **jsconfig** — якщо у пакеті є `src/`, валідує `jsconfig.json` через Rego-поліс `js_run.jsconfig` (FS-existence + canonical `compilerOptions`/`include`).
|
|
18
|
+
2. **bunyan** — сканує JS/TS джерела на заборонені імпорти `@nitra/bunyan` / `bunyan`; замінити на `@nitra/pino`.
|
|
19
|
+
3. **conn-imports** — перевіряє, що фабрики підключень (Bun SQL, mssql, graphql-request) імпортуються лише з файлів у `connDir` (`src/conn` або значення `#conn/*` з `package.json.imports`).
|
|
20
|
+
4. **conn-file-naming** — для файлів у `connDir/` (окрім `index.*`) перевіряє канон назви (`ql-<id>`, `pg-{read|write}[-<id>]`, `mysql-…`, `mssql-…`) і що єдиний іменований експорт = camelCase від basename.
|
|
21
|
+
5. **check-env** — сканує на прямий `process.env.*` (заміна — `env` з `@nitra/check-env` + `checkEnv(['…'])`) і на `env.*` без відповідного `checkEnv`.
|
|
22
|
+
6. **promise-settimeout** — шукає `new Promise(r => setTimeout(r, ms))`; замінити на `await setTimeout(ms)` з `node:timers/promises`.
|
|
23
|
+
7. **temporal** — забороняє `Temporal` API у Bun runtime; альтернатива — `Date` або інʼєктований timestamp.
|
|
24
|
+
8. **otel-configmap** — перевіряє наявність `k8s/base/configmap.yaml` (вміст перевіряє Rego `js_run.configmap`).
|
|
25
|
+
9. **conn-alias** — якщо у `connDir/` є файли, `package.json.imports["#conn/*"]` має бути оголошений.
|
|
20
26
|
|
|
21
27
|
## Публічний API
|
|
22
28
|
|
|
23
|
-
-
|
|
24
|
-
- check — перевіряє дотримання правил js-run.mdc для пакетів у workspace
|
|
29
|
+
- `check(cwd?)` — перевіряє всі workspace-пакети (виключає `.`); повертає `0` або `1`.
|
|
25
30
|
|
|
26
31
|
## Гарантії поведінки
|
|
27
32
|
|
|
28
33
|
- Read-only: файл не виконує операцій запису у файлову систему.
|
|
29
|
-
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|
|
30
|
-
- За невдачі повертає значення помилки (`false`/`null`/`Err`) замість генерування винятку чи паніки.
|
|
31
|
-
- Свідомо пропускає шляхи: `base/`.
|
|
32
34
|
- Не звертається до мережі.
|