@nitra/cursor 12.8.6 → 12.8.8
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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/rules/abie/main.mdc +9 -5
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem.mdc +22 -0
- package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches.mdc +19 -0
- package/rules/abie/policy/health_check_policy/health_check_policy.mdc +17 -0
- package/rules/abie/policy/http_route_base/http_route_base.mdc +9 -0
- package/rules/abie/policy/package_json_shared/package_json_shared.mdc +17 -0
- package/rules/adr/js/hooks.mdc +32 -0
- package/rules/adr/js/madr_format.mdc +96 -0
- package/rules/adr/js/settings_policy.mdc +34 -0
- package/rules/adr/main.mdc +17 -95
- package/rules/adr/policy/settings_json/settings_json.mdc +7 -0
- package/rules/adr/policy/settings_local_json/settings_local_json.mdc +7 -0
- package/rules/bun/js/bunfig.mdc +12 -0
- package/rules/bun/js/layout.mdc +60 -0
- package/rules/bun/js/lint.mdc +9 -0
- package/rules/bun/js/package_json.mdc +19 -0
- package/rules/bun/main.mdc +7 -60
- package/rules/bun/policy/bunfig/bunfig.mdc +12 -0
- package/rules/bun/policy/package_json/package_json.mdc +14 -0
- package/rules/capacitor/js/ios_spm.mdc +69 -0
- package/rules/capacitor/js/version.mdc +29 -0
- package/rules/capacitor/main.mdc +6 -22
- package/rules/capacitor/policy/package_json/package_json.mdc +9 -0
- package/rules/changelog/js/agent-workflow.mdc +15 -0
- package/rules/changelog/js/changelog-format.mdc +33 -0
- package/rules/changelog/js/comparison-models.mdc +40 -0
- package/rules/changelog/main.mdc +4 -98
- package/rules/ci4/js/marksman_config.mdc +31 -0
- package/rules/ci4/js/vscode_extensions.mdc +33 -0
- package/rules/ci4/main.mdc +16 -14
- package/rules/ci4/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- package/rules/docker/js/compile.mdc +44 -0
- package/rules/docker/js/hadolint.mdc +50 -0
- package/rules/docker/js/mirror.mdc +13 -0
- package/rules/docker/js/multistage.mdc +13 -0
- package/rules/docker/js/native-addon.mdc +43 -0
- package/rules/docker/js/nginx-tag.mdc +7 -0
- package/rules/docker/js/nginx-user.mdc +37 -0
- package/rules/docker/js/non-root.mdc +39 -0
- package/rules/docker/main.mdc +13 -196
- package/rules/docker/policy/lint_docker_yml/lint_docker_yml.mdc +14 -0
- package/rules/efes/main.mdc +1 -1
- package/rules/efes/policy/package_json_shared/package_json_shared.mdc +30 -0
- package/rules/ga/js/lint_toolchain.mdc +15 -0
- package/rules/ga/js/required_workflows.mdc +35 -0
- package/rules/ga/js/vscode.mdc +17 -0
- package/rules/ga/js/workflow_common.mdc +108 -0
- package/rules/ga/js/workflows.mdc +32 -0
- package/rules/ga/js/zizmor.mdc +7 -0
- package/rules/ga/main.mdc +16 -119
- package/rules/ga/policy/clean_ga_workflows/clean_ga_workflows.mdc +18 -0
- package/rules/ga/policy/clean_merged_branch/clean_merged_branch.mdc +22 -0
- package/rules/ga/policy/git_ai/git_ai.mdc +19 -0
- package/rules/ga/policy/lint_ga/lint_ga.mdc +21 -0
- package/rules/ga/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- package/rules/ga/policy/vscode_settings/vscode_settings.mdc +9 -0
- package/rules/ga/policy/workflow_common/workflow_common.mdc +18 -0
- package/rules/ga/policy/zizmor_yml/zizmor_yml.mdc +9 -0
- package/rules/graphql/js/tooling.mdc +13 -0
- package/rules/graphql/js/vscode_extensions.mdc +13 -0
- package/rules/graphql/main.mdc +4 -21
- package/rules/graphql/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- package/rules/hasura/js/internal_urls.mdc +27 -0
- package/rules/hasura/js/migrations.mdc +13 -0
- package/rules/hasura/js/svc_hl.mdc +17 -0
- package/rules/hasura/main.mdc +6 -30
- package/rules/hasura/policy/svc_hl/svc_hl.mdc +15 -0
- package/rules/image-avif/js/avif_generation.mdc +26 -0
- package/rules/image-avif/js/package_json_optout.mdc +21 -0
- package/rules/image-avif/main.mdc +5 -34
- package/rules/image-avif/policy/package_json/package_json.mdc +18 -0
- package/rules/image-compress/js/package_json.mdc +7 -0
- package/rules/image-compress/js/package_setup.mdc +13 -0
- package/rules/image-compress/main.mdc +4 -12
- package/rules/image-compress/policy/package_json/package_json.mdc +13 -0
- package/rules/js/docs/index.md +3 -3
- package/rules/js/js/dep-policy.mdc +17 -0
- package/rules/js/js/eslint-config.mdc +28 -0
- package/rules/js/js/extensions.mdc +8 -0
- package/rules/js/js/file-extensions.mdc +12 -0
- package/rules/js/js/for-in.mdc +26 -0
- package/rules/js/js/jscpd.mdc +42 -0
- package/rules/js/js/knip.mdc +15 -0
- package/rules/js/js/lint-js-workflow.mdc +58 -0
- package/rules/js/js/oxlintrc.mdc +20 -0
- package/rules/js/js/package-json.mdc +31 -0
- package/rules/js/js/tests.mdc +9 -0
- package/rules/js/js/utils-lib-structure.mdc +15 -0
- package/rules/js/main.mdc +19 -211
- package/rules/js/policy/jscpd/jscpd.mdc +14 -0
- package/rules/js/policy/lint_js_yml/lint_js_yml.mdc +14 -0
- package/rules/js/policy/package_json/package_json.mdc +15 -0
- package/rules/js/policy/vscode_extensions/vscode_extensions.mdc +11 -0
- package/rules/js-bun-db/js/bun-sql-migration.mdc +15 -0
- package/rules/js-bun-db/js/connection.mdc +42 -0
- package/rules/js-bun-db/js/pg-format-identifiers.mdc +102 -0
- package/rules/js-bun-db/js/pg-format-shim.mdc +99 -0
- package/rules/js-bun-db/js/pg-leftover.mdc +27 -0
- package/rules/js-bun-db/js/pg-listen-notify.mdc +51 -0
- package/rules/js-bun-db/js/query-safety.mdc +117 -0
- package/rules/js-bun-db/js/sql-array.mdc +88 -0
- package/rules/js-bun-db/js/unsafe.mdc +65 -0
- package/rules/js-bun-db/main.mdc +12 -607
- package/rules/js-bun-db/policy/package_json/package_json.mdc +17 -0
- package/rules/js-bun-redis/js/imports.mdc +47 -0
- package/rules/js-bun-redis/js/package_json.mdc +44 -0
- package/rules/js-bun-redis/main.mdc +4 -10
- package/rules/js-bun-redis/policy/package_json/package_json.mdc +11 -0
- package/rules/js-mssql/js/mssql-in-list.mdc +38 -0
- package/rules/js-mssql/js/mssql-pool.mdc +56 -0
- package/rules/js-mssql/js/mssql-query-template.mdc +33 -0
- package/rules/js-mssql/js/mssql-tvp.mdc +75 -0
- package/rules/js-mssql/js/mssql-version.mdc +7 -0
- package/rules/js-mssql/main.mdc +10 -198
- package/rules/js-mssql/policy/package_json/package_json.mdc +9 -0
- package/rules/js-run/js/check-env.mdc +35 -0
- package/rules/js-run/js/conn-aliases.mdc +109 -0
- package/rules/js-run/js/jsconfig.mdc +20 -0
- package/rules/js-run/js/otel-configmap.mdc +6 -0
- package/rules/js-run/js/pino.mdc +6 -0
- package/rules/js-run/js/project-structure.mdc +11 -0
- package/rules/js-run/js/runtime.mdc +14 -0
- package/rules/js-run/js/scope.mdc +11 -0
- package/rules/js-run/js/settimeout.mdc +11 -0
- package/rules/js-run/js/temporal.mdc +5 -0
- package/rules/js-run/main.mdc +16 -216
- package/rules/js-run/policy/configmap/configmap.mdc +31 -0
- package/rules/js-run/policy/jsconfig/jsconfig.mdc +25 -0
- package/rules/js-run/policy/package_json/package_json.mdc +38 -0
- package/rules/k8s/js/configmap.mdc +41 -0
- package/rules/k8s/js/deployment_resources.mdc +49 -0
- package/rules/k8s/js/hasura_httproute.mdc +91 -0
- package/rules/k8s/js/hpa_apiversion.mdc +27 -0
- package/rules/k8s/js/ingress_gateway.mdc +16 -0
- package/rules/k8s/js/kustomize_structure.mdc +144 -0
- package/rules/k8s/js/lint_k8s.mdc +72 -0
- package/rules/k8s/js/multidoc_yaml.mdc +5 -0
- package/rules/k8s/js/network_policy.mdc +136 -0
- package/rules/k8s/js/schema_modeline.mdc +57 -0
- package/rules/k8s/js/service.mdc +44 -0
- package/rules/k8s/js/topology_hpa_pdb.mdc +181 -0
- package/rules/k8s/main.mdc +29 -834
- package/rules/k8s/policy/base_kustomization/base_kustomization.mdc +12 -0
- package/rules/k8s/policy/base_manifest/base_manifest.mdc +14 -0
- package/rules/k8s/policy/gateway/gateway.mdc +17 -0
- package/rules/k8s/policy/hasura_configmap/hasura_configmap.mdc +20 -0
- package/rules/k8s/policy/hasura_httproute/hasura_httproute.mdc +16 -0
- package/rules/k8s/policy/hpa_pdb/hpa_pdb.mdc +23 -0
- package/rules/k8s/policy/kustomization/kustomization.mdc +20 -0
- package/rules/k8s/policy/manifest/manifest.mdc +17 -0
- package/rules/k8s/policy/network_policy/network_policy.mdc +22 -0
- package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml.mdc +13 -0
- package/rules/k8s/policy/svc_yaml/svc_yaml.mdc +12 -0
- package/rules/nginx-default-tpl/js/dockerfile.mdc +36 -0
- package/rules/nginx-default-tpl/js/http-route.mdc +41 -0
- package/rules/nginx-default-tpl/js/ini-keys.mdc +21 -0
- package/rules/nginx-default-tpl/js/template-structure.mdc +86 -0
- package/rules/nginx-default-tpl/js/vscode.mdc +37 -0
- package/rules/nginx-default-tpl/main.mdc +8 -110
- package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions.mdc +11 -0
- package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings.mdc +15 -0
- package/rules/npm-module/js/docs/index.md +5 -5
- package/rules/npm-module/js/docs/rule_meta.md +6 -6
- package/rules/npm-module/js/docs/skill_meta.md +8 -8
- package/rules/npm-module/js/header_doc_pointer.mdc +18 -0
- package/rules/npm-module/js/package_structure.mdc +62 -0
- package/rules/npm-module/js/rule_meta.mdc +11 -0
- package/rules/npm-module/js/skill_meta.mdc +11 -0
- package/rules/npm-module/main.mdc +10 -52
- package/rules/npm-module/policy/emit_types_config/emit_types_config.mdc +40 -0
- package/rules/npm-module/policy/npm_package_json/npm_package_json.mdc +50 -0
- package/rules/npm-module/policy/root_package_json/root_package_json.mdc +37 -0
- package/rules/php/js/lint_php_yml.mdc +12 -0
- package/rules/php/js/tooling.mdc +66 -0
- package/rules/php/main.mdc +5 -66
- package/rules/php/policy/lint_php_yml/lint_php_yml.mdc +21 -0
- package/rules/python/js/lint_python_yml.mdc +23 -0
- package/rules/python/js/pyproject_toml.mdc +32 -0
- package/rules/python/js/tooling.mdc +23 -0
- package/rules/python/main.mdc +7 -32
- package/rules/python/policy/lint_python_yml/lint_python_yml.mdc +12 -0
- package/rules/python/policy/pyproject_toml/pyproject_toml.mdc +13 -0
- package/rules/rego/js/rego-lint.mdc +31 -0
- package/rules/rego/js/vscode_extensions.mdc +11 -0
- package/rules/rego/js/vscode_settings.mdc +13 -0
- package/rules/rego/main.mdc +10 -22
- package/rules/rego/policy/vscode_extensions/vscode_extensions.mdc +11 -0
- package/rules/rego/policy/vscode_settings/vscode_settings.mdc +19 -0
- package/rules/rust/js/coverage.mdc +28 -0
- package/rules/rust/js/lint.mdc +22 -0
- package/rules/rust/js/tauri_composition.mdc +8 -0
- package/rules/rust/js/vscode_extensions.mdc +12 -0
- package/rules/rust/main.mdc +8 -38
- package/rules/rust/policy/lint_rust_yml/lint_rust_yml.mdc +12 -0
- package/rules/rust/policy/vscode_extensions/vscode_extensions.mdc +9 -0
- package/rules/security/js/rego_policies.mdc +15 -0
- package/rules/security/js/sample_secret.mdc +19 -0
- package/rules/security/js/trufflehog.mdc +21 -0
- package/rules/security/main.mdc +7 -34
- package/rules/security/policy/lint_security_yml/lint_security_yml.mdc +7 -0
- package/rules/security/policy/package_json/package_json.mdc +7 -0
- package/rules/style/js/admin-table.mdc +88 -0
- package/rules/style/js/colors.mdc +21 -0
- package/rules/style/js/gap.mdc +22 -0
- package/rules/style/js/quasar-fixes.mdc +32 -0
- package/rules/style/js/quasar.mdc +7 -0
- package/rules/style/js/tooling.mdc +85 -0
- package/rules/style/main.mdc +12 -251
- package/rules/style/policy/lint_style_yml/lint_style_yml.mdc +13 -0
- package/rules/style/policy/package_json/package_json.mdc +18 -0
- package/rules/style/policy/vscode_extensions/vscode_extensions.mdc +13 -0
- package/rules/style/policy/vscode_settings/vscode_settings.mdc +19 -0
- package/rules/tauri/js/cargo_mutants_config.mdc +39 -0
- package/rules/tauri/js/tool_surface.mdc +21 -0
- package/rules/tauri/js/tooling.mdc +25 -0
- package/rules/tauri/main.mdc +6 -78
- package/rules/tauri/policy/vscode_extensions/vscode_extensions.mdc +21 -0
- package/rules/test/js/cargo_mutants_config.mdc +18 -0
- package/rules/test/js/docs/index.md +7 -7
- package/rules/test/js/location.mdc +52 -0
- package/rules/test/js/no-console-store-restore.mdc +11 -0
- package/rules/test/js/no-process-chdir.mdc +15 -0
- package/rules/test/js/no-relative-fs-path.mdc +22 -0
- package/rules/test/js/sandbox-aware-test.mdc +28 -0
- package/rules/test/js/stryker_config.mdc +26 -0
- package/rules/test/js/vitest-config-pool-forks.mdc +33 -0
- package/rules/test/main.mdc +16 -184
- package/rules/test/policy/package_json/package_json.mdc +16 -0
- package/rules/text/js/ci-lint-text.mdc +15 -0
- package/rules/text/js/cspell.mdc +81 -0
- package/rules/text/js/dotenv-linter.mdc +16 -0
- package/rules/text/js/forbidden-prettier.mdc +13 -0
- package/rules/text/js/markdownlint.mdc +25 -0
- package/rules/text/js/oxfmt.mdc +35 -0
- package/rules/text/js/package-json.mdc +26 -0
- package/rules/text/js/shellcheck.mdc +18 -0
- package/rules/text/js/v8r.mdc +23 -0
- package/rules/text/js/vscode.mdc +86 -0
- package/rules/text/main.mdc +20 -231
- package/rules/text/policy/cspell/cspell.mdc +34 -0
- package/rules/text/policy/lint_text/lint_text.mdc +19 -0
- package/rules/text/policy/markdownlint/markdownlint.mdc +38 -0
- package/rules/text/policy/oxfmtrc/oxfmtrc.mdc +11 -0
- package/rules/text/policy/package_json/package_json.mdc +33 -0
- package/rules/text/policy/vscode_extensions/vscode_extensions.mdc +13 -0
- package/rules/text/policy/vscode_settings/vscode_settings.mdc +13 -0
- package/rules/vue/js/composition-api.mdc +82 -0
- package/rules/vue/js/nheader-layout.mdc +171 -0
- package/rules/vue/js/node-imports.mdc +25 -0
- package/rules/vue/js/quasar-ui.mdc +32 -0
- package/rules/vue/js/structure.mdc +101 -0
- package/rules/vue/js/testing.mdc +32 -0
- package/rules/vue/js/tfm-translations.mdc +26 -0
- package/rules/vue/js/vite-config.mdc +126 -0
- package/rules/vue/js/vite-env.mdc +55 -0
- package/rules/vue/js/vue-imports.mdc +25 -0
- package/rules/vue/main.mdc +15 -641
- package/rules/vue/policy/package_json/package_json.mdc +30 -0
- package/scripts/docs/index.md +16 -16
- package/scripts/lib/docs/index.md +36 -36
- package/scripts/utils/docs/index.md +14 -14
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
## Сканування заборонених redis-імпортів у JS/TS-джерелах
|
|
2
|
+
|
|
3
|
+
Правило сканує всі JS/TS-файли проєкту (`.js`, `.ts`, `.mjs`, `.cjs`, `.jsx`, `.tsx` тощо, без `.d.ts`) на наявність `import`, `require()` або динамічного `import()` з заборонених пакетів:
|
|
4
|
+
|
|
5
|
+
- `ioredis` та підшляхи (`ioredis/…`)
|
|
6
|
+
- `node-redis`
|
|
7
|
+
- `redis` та підшляхи (`redis/…`)
|
|
8
|
+
- `@redis/client`, `@redis/json`, `@redis/search`, `@redis/time-series`, `@redis/bloom`
|
|
9
|
+
|
|
10
|
+
Усі ці пакети слід замінити на **Bun native Redis**: `import { redis } from 'bun'` — документація: <https://bun.com/docs/runtime/redis>.
|
|
11
|
+
|
|
12
|
+
Сканування виконується через **oxc-parser** по AST (не regex по тексту) — статичні імпорти, `require()`-виклики і динамічні `import()` окремо.
|
|
13
|
+
|
|
14
|
+
При синтаксичних помилках у файлі результат — порожній (спочатку виправити синтаксис).
|
|
15
|
+
|
|
16
|
+
Директорії, зазначені у `.n-cursor-ignore` / `cursorignore` конфігурації, повністю пропускаються.
|
|
17
|
+
|
|
18
|
+
### Повідомлення про порушення
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
js-bun-redis: src/conn/redis.ts:3 — заміни 'ioredis' на Bun native Redis (import { redis } from 'bun', https://bun.com/docs/runtime/redis): import IORedis from 'ioredis'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Міграція
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// До (ioredis)
|
|
28
|
+
import IORedis from 'ioredis'
|
|
29
|
+
const client = new IORedis({ host: 'localhost', port: 6379 })
|
|
30
|
+
await client.set('key', 'value')
|
|
31
|
+
|
|
32
|
+
// Після (Bun native Redis)
|
|
33
|
+
import { redis } from 'bun'
|
|
34
|
+
await redis.set('key', 'value')
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// До (node-redis / redis v4)
|
|
39
|
+
import { createClient } from 'redis'
|
|
40
|
+
const client = createClient()
|
|
41
|
+
await client.connect()
|
|
42
|
+
await client.set('key', 'value')
|
|
43
|
+
|
|
44
|
+
// Після (Bun native Redis)
|
|
45
|
+
import { redis } from 'bun'
|
|
46
|
+
await redis.set('key', 'value')
|
|
47
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
## Перевірка `dependencies` у `package.json` (Rego gate)
|
|
2
|
+
|
|
3
|
+
Rego-полісі `js_bun_redis.package_json` перевіряє `package.json` кожного воркспейсу на наявність заборонених redis-залежностей у полі `dependencies`.
|
|
4
|
+
|
|
5
|
+
**Namespace:** `js_bun_redis.package_json`
|
|
6
|
+
|
|
7
|
+
Канон заборонених пакетів надходить через `--data` з файлу-шаблону:
|
|
8
|
+
|
|
9
|
+
[package.json.deny.json](./policy/package_json/template/package.json.deny.json)
|
|
10
|
+
|
|
11
|
+
Поточний список заборонених `dependencies`:
|
|
12
|
+
|
|
13
|
+
| Пакет | Причина |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `ioredis` | заміни на Bun native Redis |
|
|
16
|
+
| `node-redis` | заміни на Bun native Redis |
|
|
17
|
+
| `redis` | заміни на Bun native Redis |
|
|
18
|
+
| `@redis/client` | заміни на Bun native Redis |
|
|
19
|
+
| `@redis/json` | заміни на Bun native Redis |
|
|
20
|
+
| `@redis/search` | заміни на Bun native Redis |
|
|
21
|
+
| `@redis/time-series` | заміни на Bun native Redis |
|
|
22
|
+
| `@redis/bloom` | заміни на Bun native Redis |
|
|
23
|
+
|
|
24
|
+
### Повідомлення про порушення
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
dependencies.ioredis — заміни на Bun native Redis (js-bun-redis.mdc)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Виправлення
|
|
31
|
+
|
|
32
|
+
```jsonc
|
|
33
|
+
// package.json — видалити зі списку dependencies:
|
|
34
|
+
{
|
|
35
|
+
"dependencies": {
|
|
36
|
+
// "ioredis": "...", ← видалити
|
|
37
|
+
// "node-redis": "...", ← видалити
|
|
38
|
+
// "redis": "...", ← видалити
|
|
39
|
+
// "@redis/client": "...",← видалити
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Після видалення пакетів з `dependencies` виконати також сканування джерел на заборонені імпорти — перевірка `js-bun-redis-imports`.
|
|
@@ -5,16 +5,10 @@ alwaysApply: false
|
|
|
5
5
|
version: '1.2'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Правило забороняє використання застарілих redis-клієнтів (`ioredis`, `node-redis`, `redis`, `@redis/*`) і вимагає переходу на **Bun native Redis** (`import { redis } from 'bun'`). Підтримувана версія Redis: **7.2+**.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
[js-bun-redis-imports](./js/imports.mdc)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
[js-bun-redis-package_json](./js/package_json.mdc)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- Видалити з `dependencies`: `ioredis`, `node-redis`.
|
|
17
|
-
- Видалити з коду: усі `import` / `require` цих пакетів та власні обгортки над ними.
|
|
18
|
-
- Замінити на `import { redis } from 'bun'`
|
|
19
|
-
|
|
20
|
-
Канон заборонених `dependencies` (`ioredis`, `node-redis`, `redis`, `@redis/*`): [package.json.deny.json](./policy/package_json/template/package.json.deny.json).
|
|
14
|
+
[js-bun-redis-policy-package_json](./policy/package_json/package_json.mdc)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Rego gate: заборонені redis-залежності у `package.json`
|
|
2
|
+
|
|
3
|
+
Rego-пакет: `js-bun-redis.package_json`
|
|
4
|
+
|
|
5
|
+
**Цільовий файл:** `package.json` (поле `dependencies`)
|
|
6
|
+
|
|
7
|
+
Перевіряє наявність заборонених redis-пакетів у `dependencies`. Список заборон надходить через `--data` з канонічного шаблону:
|
|
8
|
+
|
|
9
|
+
[package.json.deny.json](./template/package.json.deny.json)
|
|
10
|
+
|
|
11
|
+
Повідомлення про порушення формується як `dependencies.<pkg> — <reason>`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## Парсинг і guard для `IN (${...})`
|
|
2
|
+
|
|
3
|
+
Якщо `IN (...)` все ж використовується (а не `JOIN` на TVP), значення в `${...}` **обов'язково** мають бути попередньо приведені числовим парсером і відфільтровані від `NaN`.
|
|
4
|
+
|
|
5
|
+
Це знімає будь-яку можливість SQL injection: SQL-метасимволи в `Number`/`parseInt(...)` перетворюються на `NaN` і відсіюються.
|
|
6
|
+
|
|
7
|
+
### Правила
|
|
8
|
+
|
|
9
|
+
Додатково:
|
|
10
|
+
|
|
11
|
+
- значення для `IN (${...})` потрібно **винести в окрему змінну** перед запитом (не підставляти вираз напряму в `${...}`);
|
|
12
|
+
- цю змінну потрібно **перевірити на пустоту** і якщо список порожній — **throw error** (щоб не виконувати некоректний запит).
|
|
13
|
+
|
|
14
|
+
### Приклади
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
// ❌ НЕ МОЖНА: значення з req.body / зовнішнього джерела без парсингу
|
|
18
|
+
const outIds = pgQ.rows.flatMap(x => x.req_body.Orders.map(o => o.OutletId))
|
|
19
|
+
await pool.query(/* sql */ String.raw`
|
|
20
|
+
SELECT ... WHERE so.OutletId IN (${outIds})
|
|
21
|
+
`)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// ✅ МОЖНА: parseInt + filter(!isNaN) гарантує, що в SQL потраплять лише числа
|
|
26
|
+
// і перед запитом робимо guard на пустоту, щоб не виконувати некоректний SQL.
|
|
27
|
+
const outIds = pgQ.rows
|
|
28
|
+
.flatMap(x => x.req_body.Orders.map(o => parseInt(o.OutletId)))
|
|
29
|
+
.filter(n => !isNaN(n))
|
|
30
|
+
if (!outIds.length) throw new Error('outIds is empty')
|
|
31
|
+
await pool.request().query`
|
|
32
|
+
SELECT ... WHERE so.OutletId IN (${outIds})
|
|
33
|
+
`
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Допустимі парсери: `parseInt(...)`, `parseFloat(...)`, `Number(...)`, `BigInt(...)` або унарний `+x`. Літеральні масиви чисел (`[1, 2, 3]`) теж безпечні — без парсера, але без жодних рядків.
|
|
37
|
+
|
|
38
|
+
Це правило діє і для безпечного `pool.request().query\`...\`` (де mssql сам параметризує масив), і поготів для `pool.query(String.raw\`...\`)` чи `pool.query(\`...\`)`, де такий парсинг — єдиний бар'єр.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## Singleton ConnectionPool і заборона shared Request
|
|
2
|
+
|
|
3
|
+
Потрібно використовувати connection pool (`sql.ConnectionPool`) як singleton, а **НЕ** створювати підключення на кожен запит.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
// db.js
|
|
7
|
+
import sql from 'mssql';
|
|
8
|
+
|
|
9
|
+
let poolPromise;
|
|
10
|
+
|
|
11
|
+
export function getPool() {
|
|
12
|
+
if (!poolPromise) {
|
|
13
|
+
const db = new sql.ConnectionPool(config);
|
|
14
|
+
poolPromise = db.connect().catch(err => {
|
|
15
|
+
poolPromise = undefined; // дозволити повторну спробу
|
|
16
|
+
throw err;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return poolPromise;
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
// usage
|
|
25
|
+
import { getPool } from './db.js';
|
|
26
|
+
|
|
27
|
+
const pool = await getPool();
|
|
28
|
+
const userId = 42;
|
|
29
|
+
const status = 'active';
|
|
30
|
+
|
|
31
|
+
// Tagged template на request — це працює
|
|
32
|
+
const result = await pool.request().query`
|
|
33
|
+
SELECT * FROM users
|
|
34
|
+
WHERE id = ${userId} AND status = ${status}
|
|
35
|
+
`;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Ключове: `pool.request().query\`...\`` — бектіки після `query`, без круглих дужок. Це tagged template, де mssql параметризує значення автоматично.
|
|
39
|
+
|
|
40
|
+
### Заборона shared Request
|
|
41
|
+
|
|
42
|
+
Заборонено робити singleton `request` на рівні модуля:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// ❌ НЕ МОЖНА
|
|
46
|
+
export const request = pool.request();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`Request` має створюватися **щоразу заново**:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
// ✅ МОЖНА
|
|
53
|
+
const request = pool.request();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Це особливо важливо для **TVP** (табличні параметри) та будь-яких `.input(...)`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## Безпечний tagged template vs небезпечний `query(...)`
|
|
2
|
+
|
|
3
|
+
Різниця між `query\`...\`` і `query(\`...\`)` — критична.
|
|
4
|
+
|
|
5
|
+
### Що НЕ робити: `query(\`...\`)`
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
// ❌ Це не tagged template — це конкатенація рядка перед викликом
|
|
9
|
+
await pool.request().query(`SELECT * FROM users WHERE id = ${userId}`);
|
|
10
|
+
// ↑ круглі дужки замість бектіків = звичайна інтерполяція = SQL injection
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Що робити: `query\`...\``
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
// ✅ Tagged template — mssql параметризує ${userId} автоматично
|
|
17
|
+
await pool.request().query`SELECT * FROM users WHERE id = ${userId}`;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Коментар під час виправлення SQL injection
|
|
21
|
+
|
|
22
|
+
Коли виправляєш місце з потенційним SQL injection (заміна `query(\`...\`)` на `query\`...\`` або прибирання динамічних списків/конкатенації), **додай поруч короткий коментар** з описом причини.
|
|
23
|
+
|
|
24
|
+
Вимоги до коментаря:
|
|
25
|
+
|
|
26
|
+
- вказати **що було небезпечно** (звичайна інтерполяція в рядок, конкатенація, динамічний список);
|
|
27
|
+
- вказати **чому новий варіант безпечний** (tagged template / параметризація / TVP);
|
|
28
|
+
- 1–2 рядки, без дублювання очевидного.
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// SQLi fix: query`...` (tagged template) параметризує значення; query(`...`) небезпечний через інтерполяцію.
|
|
32
|
+
await pool.request().query`SELECT * FROM users WHERE id = ${userId}`
|
|
33
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
## TVP (table-valued parameters) — рекомендований підхід для списків
|
|
2
|
+
|
|
3
|
+
TVP дозволений і рекомендований для SQL Server 2019. Для списків значень та пар ключів **найкращий** шлях по безпеці/продуктивності — TVP.
|
|
4
|
+
|
|
5
|
+
### 1) `IN (...)` для кодів → `JOIN` на TVP
|
|
6
|
+
|
|
7
|
+
Замість складання SQL-рядка:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// ❌ НЕ МОЖНА: динамічний список у SQL
|
|
11
|
+
await pool.request().query`
|
|
12
|
+
SELECT *
|
|
13
|
+
FROM promo.SomeTable t
|
|
14
|
+
WHERE t.ExternalCode IN (${codes.map(c => `'${c}'`).join(',')})
|
|
15
|
+
`;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Роби TVP з 1 колонкою:
|
|
19
|
+
|
|
20
|
+
- створити `new sql.Table()`
|
|
21
|
+
- `columns.add('ExternalCode', sql.NVarChar(N))`
|
|
22
|
+
- `rows.add(code)` після `trim()` + валідації
|
|
23
|
+
- `request.input('codes', table)`
|
|
24
|
+
- в SQL: `JOIN @codes c ON c.ExternalCode = t.ExternalCode`
|
|
25
|
+
|
|
26
|
+
Плюси:
|
|
27
|
+
|
|
28
|
+
- 0% SQL injection
|
|
29
|
+
- текст SQL **не росте** від довжини масиву
|
|
30
|
+
- SQL Server часто оптимізує `JOIN` краще, ніж гігантський `IN (...)`
|
|
31
|
+
|
|
32
|
+
### 2) `DELETE ... VALUES (...)` / `MERGE ... VALUES (...)` → TVP з 2 колонками
|
|
33
|
+
|
|
34
|
+
Замість генерації списку рядків `(...),(...),...` у SQL (навіть якщо воно "через tagged template"):
|
|
35
|
+
|
|
36
|
+
- сформуй TVP-таблицю `@Pairs` з колонками:
|
|
37
|
+
- `PromoActivitiesId` (INT або BIGINT — як у схемі БД)
|
|
38
|
+
- `SupplierOutletId` (INT або BIGINT — як у схемі БД)
|
|
39
|
+
|
|
40
|
+
Delete:
|
|
41
|
+
|
|
42
|
+
```sql
|
|
43
|
+
DELETE abi
|
|
44
|
+
FROM promo.ActivitiesByOutlet abi
|
|
45
|
+
JOIN @Pairs p
|
|
46
|
+
ON p.PromoActivitiesId = abi.PromoActivitiesId
|
|
47
|
+
AND p.SupplierOutletId = abi.SupplierOutletId;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Insert (без `MERGE`, простіше і безпечніше):
|
|
51
|
+
|
|
52
|
+
```sql
|
|
53
|
+
INSERT INTO promo.ActivitiesByOutlet (PromoActivitiesId, SupplierOutletId, Status)
|
|
54
|
+
SELECT p.PromoActivitiesId, p.SupplierOutletId, 2
|
|
55
|
+
FROM @Pairs p
|
|
56
|
+
WHERE NOT EXISTS (
|
|
57
|
+
SELECT 1
|
|
58
|
+
FROM promo.ActivitiesByOutlet abi
|
|
59
|
+
WHERE abi.PromoActivitiesId = p.PromoActivitiesId
|
|
60
|
+
AND abi.SupplierOutletId = p.SupplierOutletId
|
|
61
|
+
);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Плюси:
|
|
65
|
+
|
|
66
|
+
- не збираєш SQL-рядок із даних
|
|
67
|
+
- менше шансів упіймати edge-case `MERGE` (у SQL Server історично багато "сюрпризів")
|
|
68
|
+
|
|
69
|
+
### Мінімальна валідація перед наповненням TVP
|
|
70
|
+
|
|
71
|
+
Навіть з TVP потрібно:
|
|
72
|
+
|
|
73
|
+
- `ExternalCode`: `typeof === 'string'`, `trim()`, довжина `<= N` (під схему), відкинути пусті.
|
|
74
|
+
- ліміт на кількість елементів (наприклад 5k/10k — залежить від вашого потоку).
|
|
75
|
+
- `supplierId`/ID: число/BigInt, валідне та скінченне.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## Версія пакета `mssql`
|
|
2
|
+
|
|
3
|
+
Якщо в проекті в будь-якому `package.json` в секції `dependencies` присутній пакет **`mssql`**, то його версія повинна бути не менше **12.5.0**.
|
|
4
|
+
|
|
5
|
+
Правило орієнтоване на **SQL Server 2019**.
|
|
6
|
+
|
|
7
|
+
Допустимі формати діапазону: `^12.5.0`, `>=12.5.0`, `12.5.0`, `workspace:*` (трактується як OK).
|
package/rules/js-mssql/main.mdc
CHANGED
|
@@ -5,210 +5,22 @@ alwaysApply: false
|
|
|
5
5
|
version: '1.4'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Правило охоплює безпечне використання пакету `mssql` у Node.js: мінімальну версію залежності, singleton ConnectionPool, заборону небезпечних шаблонів запитів і захист від SQL injection через TVP або числовий парсинг IN-списків.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
[js-mssql-mssql-version](./js/mssql-version.mdc)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
[js-mssql-mssql-pool](./js/mssql-pool.mdc)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
[js-mssql-mssql-query-template](./js/mssql-query-template.mdc)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
[js-mssql-mssql-tvp](./js/mssql-tvp.mdc)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
[js-mssql-mssql-in-list](./js/mssql-in-list.mdc)
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
## Швидкий gate через conftest
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
javascript// db.js
|
|
24
|
-
import sql from 'mssql';
|
|
22
|
+
Rego-перевірки (`policy/`) — швидкий синхронний gate для одиничного `package.json` без JS-рантайму:
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
[js-mssql-package_json](./policy/package_json/package_json.mdc)
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
if (!poolPromise) {
|
|
30
|
-
const db = new SQL.ConnectionPool(config);
|
|
31
|
-
poolPromise = pool.connect().catch(err => {
|
|
32
|
-
poolPromise = undefined; // дозволити повторну спробу
|
|
33
|
-
throw err;
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return poolPromise;
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
// usage
|
|
42
|
-
import { getPool } from './db.js';
|
|
43
|
-
|
|
44
|
-
const pool = await getPool();
|
|
45
|
-
const userId = 42;
|
|
46
|
-
const status = 'active';
|
|
47
|
-
|
|
48
|
-
// Tagged template на request — це працює
|
|
49
|
-
const result = await pool.request().query`
|
|
50
|
-
SELECT * FROM users
|
|
51
|
-
WHERE id = ${userId} AND status = ${status}
|
|
52
|
-
`;
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Ключове: pool.request().query\...`— бекті́ки післяquery`, без круглих дужок. Це той самий tagged template, тільки контекст — конкретний пул, а не глобальний.
|
|
57
|
-
|
|
58
|
-
## Коментар під час виправлення SQL injection
|
|
59
|
-
|
|
60
|
-
Коли виправляєш місце з потенційним **SQL injection** (наприклад, заміна `query(\`...\`)` на `query\`...\`` або прибирання динамічних списків/конкатенації), **додай поруч короткий коментар** з описом причини.
|
|
61
|
-
|
|
62
|
-
Вимоги до коментаря:
|
|
63
|
-
|
|
64
|
-
- вказати **що було небезпечно** (звичайна інтерполяція в рядок, конкатенація, динамічний список);
|
|
65
|
-
- вказати **чому новий варіант безпечний** (tagged template / параметризація / TVP);
|
|
66
|
-
- 1–2 рядки, без дублювання очевидного.
|
|
67
|
-
|
|
68
|
-
Приклад:
|
|
69
|
-
|
|
70
|
-
```javascript
|
|
71
|
-
// SQLi fix: query`...` (tagged template) параметризує значення; query(`...`) небезпечний через інтерполяцію.
|
|
72
|
-
await pool.request().query`SELECT * FROM users WHERE id = ${userId}`
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Що НЕ робити
|
|
76
|
-
|
|
77
|
-
### Не робити `query(\`...\`)`
|
|
78
|
-
|
|
79
|
-
javascript// ❌ Це не tagged template — це конкатенація рядка перед викликом
|
|
80
|
-
|
|
81
|
-
```javascript
|
|
82
|
-
await pool.request().query(`SELECT * FROM users WHERE id = ${userId}`);
|
|
83
|
-
// ↑ круглі дужки замість бекті́ків = звичайна інтерполяція = SQL injection
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Різниця між query\...`іquery(`...`)` — критична. Перше безпечне, друге — діра.
|
|
87
|
-
Потрібно використовувати перше. Потрібно шукати в коді друге і заміняти на перше.
|
|
88
|
-
|
|
89
|
-
### Не шарити `Request` між запитами
|
|
90
|
-
|
|
91
|
-
Заборонено робити singleton `request` на рівні модуля на кшталт:
|
|
92
|
-
|
|
93
|
-
```javascript
|
|
94
|
-
// ❌ НЕ МОЖНА
|
|
95
|
-
export const request = pool.request();
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
`Request` має створюватися **щоразу заново**:
|
|
99
|
-
|
|
100
|
-
```javascript
|
|
101
|
-
// ✅ МОЖНА
|
|
102
|
-
const request = pool.request();
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Це особливо важливо для **TVP** (табличні параметри) та будь-яких `.input(...)`.
|
|
106
|
-
|
|
107
|
-
## TVP дозволений і рекомендований (SQL Server 2019)
|
|
108
|
-
|
|
109
|
-
Для списків значень та пар ключів **найкращий** шлях по безпеці/продуктивності — **TVP** (table-valued parameters).
|
|
110
|
-
|
|
111
|
-
### 1) `IN (...)` для кодів → `JOIN` на TVP
|
|
112
|
-
|
|
113
|
-
Замість складання SQL-рядка:
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
// ❌ НЕ МОЖНА: динамічний список у SQL
|
|
117
|
-
await pool.request().query`
|
|
118
|
-
SELECT *
|
|
119
|
-
FROM promo.SomeTable t
|
|
120
|
-
WHERE t.ExternalCode IN (${codes.map(c => `'${c}'`).join(',')})
|
|
121
|
-
`;
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Роби TVP з 1 колонкою:
|
|
125
|
-
|
|
126
|
-
- створити `new sql.Table()`
|
|
127
|
-
- `columns.add('ExternalCode', sql.NVarChar(N))`
|
|
128
|
-
- `rows.add(code)` після `trim()` + валідації
|
|
129
|
-
- `request.input('codes', table)`
|
|
130
|
-
- в SQL: `JOIN @codes c ON c.ExternalCode = t.ExternalCode`
|
|
131
|
-
|
|
132
|
-
Плюси:
|
|
133
|
-
|
|
134
|
-
- 0% SQL injection
|
|
135
|
-
- текст SQL **не росте** від довжини масиву
|
|
136
|
-
- SQL Server часто оптимізує `JOIN` краще, ніж гігантський `IN (...)`
|
|
137
|
-
|
|
138
|
-
### 2) `DELETE ... VALUES (...)` / `MERGE ... VALUES (...)` → TVP з 2 колонками
|
|
139
|
-
|
|
140
|
-
Замість генерації списку рядків `(...),(...),...` у SQL (навіть якщо воно “через tagged template”):
|
|
141
|
-
|
|
142
|
-
- сформуй TVP-таблицю `@Pairs` з колонками:
|
|
143
|
-
- `PromoActivitiesId` (INT або BIGINT — як у схемі БД)
|
|
144
|
-
- `SupplierOutletId` (INT або BIGINT — як у схемі БД)
|
|
145
|
-
|
|
146
|
-
Delete:
|
|
147
|
-
|
|
148
|
-
```sql
|
|
149
|
-
DELETE abi
|
|
150
|
-
FROM promo.ActivitiesByOutlet abi
|
|
151
|
-
JOIN @Pairs p
|
|
152
|
-
ON p.PromoActivitiesId = abi.PromoActivitiesId
|
|
153
|
-
AND p.SupplierOutletId = abi.SupplierOutletId;
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Insert (без `MERGE`, простіше і безпечніше):
|
|
157
|
-
|
|
158
|
-
```sql
|
|
159
|
-
INSERT INTO promo.ActivitiesByOutlet (PromoActivitiesId, SupplierOutletId, Status)
|
|
160
|
-
SELECT p.PromoActivitiesId, p.SupplierOutletId, 2
|
|
161
|
-
FROM @Pairs p
|
|
162
|
-
WHERE NOT EXISTS (
|
|
163
|
-
SELECT 1
|
|
164
|
-
FROM promo.ActivitiesByOutlet abi
|
|
165
|
-
WHERE abi.PromoActivitiesId = p.PromoActivitiesId
|
|
166
|
-
AND abi.SupplierOutletId = p.SupplierOutletId
|
|
167
|
-
);
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Плюси:
|
|
171
|
-
|
|
172
|
-
- не збираєш SQL-рядок із даних
|
|
173
|
-
- менше шансів упіймати edge-case `MERGE` (у SQL Server історично багато “сюрпризів”)
|
|
174
|
-
|
|
175
|
-
## Мінімальна валідація перед наповненням TVP
|
|
176
|
-
|
|
177
|
-
Навіть з TVP потрібно:
|
|
178
|
-
|
|
179
|
-
- `ExternalCode`: `typeof === 'string'`, `trim()`, довжина `<= N` (під схему), відкинути пусті.
|
|
180
|
-
- ліміт на кількість елементів (наприклад 5k/10k — залежить від вашого потоку).
|
|
181
|
-
- `supplierId`/ID: число/BigInt, валідне та скінченне.
|
|
182
|
-
|
|
183
|
-
## Парсинг значень для `IN (${...})`
|
|
184
|
-
|
|
185
|
-
Якщо `IN (...)` все ж використовується (а не `JOIN` на TVP), значення в `${...}` **обовʼязково** мають бути попередньо приведені числовим парсером і відфільтровані від `NaN`. Це знімає будь-яку можливість SQL injection: SQL-метасимволи в `Number`/`parseInt(...)` перетворюються на `NaN` і відсіюються.
|
|
186
|
-
|
|
187
|
-
Додатково:
|
|
188
|
-
|
|
189
|
-
- значення для `IN (${...})` потрібно **винести в окрему змінну** перед запитом (не підставляти вираз напряму в `${...}`);
|
|
190
|
-
- цю змінну потрібно **перевірити на пустоту** і якщо список порожній — **throw error** (щоб не виконувати некоректний запит).
|
|
191
|
-
|
|
192
|
-
```javascript
|
|
193
|
-
// ❌ НЕ МОЖНА: значення з req.body / зовнішнього джерела без парсингу
|
|
194
|
-
const outIds = pgQ.rows.flatMap(x => x.req_body.Orders.map(o => o.OutletId))
|
|
195
|
-
await pool.query(/* sql */ String.raw`
|
|
196
|
-
SELECT ... WHERE so.OutletId IN (${outIds})
|
|
197
|
-
`)
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
```javascript
|
|
201
|
-
// ✅ МОЖНА: parseInt + filter(!isNaN) гарантує, що в SQL потраплять лише числа
|
|
202
|
-
// і перед запитом робимо guard на пустоту, щоб не виконувати некоректний SQL.
|
|
203
|
-
const outIds = pgQ.rows
|
|
204
|
-
.flatMap(x => x.req_body.Orders.map(o => parseInt(o.OutletId)))
|
|
205
|
-
.filter(n => !isNaN(n))
|
|
206
|
-
if (!outIds.length) throw new Error('outIds is empty')
|
|
207
|
-
await pool.request().query`
|
|
208
|
-
SELECT ... WHERE so.OutletId IN (${outIds})
|
|
209
|
-
`
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Допустимі парсери: `parseInt(...)`, `parseFloat(...)`, `Number(...)`, `BigInt(...)` або унарний `+x`. Літеральні масиви чисел (`[1, 2, 3]`) теж безпечні — без парсера, але без жодних рядків.
|
|
213
|
-
|
|
214
|
-
Це правило діє і для безпечного `pool.request().query\`...\`` (де mssql сам параметризує масив), і поготів для `pool.query(String.raw\`...\`)` чи `pool.query(\`...\`)`, де такий парсинг — єдиний бар'єр.
|
|
26
|
+
JS-перевірка (`deps.mjs`) — authoritative: охоплює всі `package.json` репо, AST-скан JS/TS-джерел і повний semver-triple-compare. Rego-gate — доповнення, не заміна.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## Rego-gate: версія `mssql` у `package.json`
|
|
2
|
+
|
|
3
|
+
Rego-пакет: `js_mssql.package_json`
|
|
4
|
+
|
|
5
|
+
Цільові файли: `**/package.json`
|
|
6
|
+
|
|
7
|
+
Перевіряє поле `dependencies.mssql` — якщо присутнє, версія має бути `>= 12.5.0`. Підтримувані формати: `^12.5.0`, `>=12.5.0`, `12.5.0`, `workspace:*` (OK). Версії нижче 12.5.x генерують `deny`.
|
|
8
|
+
|
|
9
|
+
Це швидкий синхронний gate для одиничного `package.json`. Authoritative-перевірка (повний semver-triple-compare, усі `package.json` репо) — у JS (`fix.mjs`).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## CheckEnv та заборона прямого `process.env`
|
|
2
|
+
|
|
3
|
+
### CheckEnv
|
|
4
|
+
|
|
5
|
+
Усі змінні оточення, які використовуються в коді, повинні бути перевірені за допомогою `checkEnv` з пакету `@nitra/check-env`. Це гарантує, що всі необхідні змінні оточення встановлені перед запуском програми.
|
|
6
|
+
|
|
7
|
+
```javascript title="Приклад підключення до PostgreSQL в /src/conn/pg.mjs"
|
|
8
|
+
import { checkEnv, env } from '@nitra/check-env'
|
|
9
|
+
import { SQL } from 'bun'
|
|
10
|
+
|
|
11
|
+
checkEnv(['PG_CONN'])
|
|
12
|
+
|
|
13
|
+
export const db = new SQL({ url: env.PG_CONN })
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### process.env
|
|
18
|
+
|
|
19
|
+
Прямий доступ до `process.env.X` у коді заборонений — його треба замінити на `env`:
|
|
20
|
+
|
|
21
|
+
> Стосується лише backend-пакетів (див. **Область застосування**). У frontend-пакетах (`vite` у `devDependencies`) — **не змінюй** `process.env.*` і **не додавай** імпорт `node:process`.
|
|
22
|
+
|
|
23
|
+
- **обов'язкова змінна** — `import { checkEnv, env } from '@nitra/check-env'` плюс `checkEnv(['X'])`
|
|
24
|
+
у тому ж файлі (приклад див. вище в розділі **CheckEnv**);
|
|
25
|
+
- **опційна змінна** — `import { env } from 'node:process'`:
|
|
26
|
+
|
|
27
|
+
```javascript title="Опційна змінна — env з node:process"
|
|
28
|
+
import { env } from 'node:process'
|
|
29
|
+
|
|
30
|
+
console.log(env.OPTIONAL_ENV_VAR)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Тимчасово приглушити перевірку для конкретного рядка можна коментарем
|
|
34
|
+
`// @nitra/cursor ignore-next-line checkEnv` безпосередньо перед використанням
|
|
35
|
+
(escape-hatch для legacy-коду, не для нових файлів).
|