fchek 1.0.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/README.md +64 -0
- package/bin/fchek.js +107 -0
- package/lib/api.js +110 -0
- package/lib/audit.js +211 -0
- package/lib/bench.js +248 -0
- package/lib/config.js +191 -0
- package/lib/context.js +356 -0
- package/lib/convention.js +526 -0
- package/lib/coverage.js +604 -0
- package/lib/db.js +135 -0
- package/lib/deps-check.js +264 -0
- package/lib/deps.js +374 -0
- package/lib/docker.js +84 -0
- package/lib/doctor.js +149 -0
- package/lib/dom.js +226 -0
- package/lib/fuzz.js +470 -0
- package/lib/git.js +290 -0
- package/lib/goto.js +544 -0
- package/lib/launch.js +182 -0
- package/lib/lint.js +624 -0
- package/lib/new_features.test.js +181 -0
- package/lib/output.js +46 -0
- package/lib/port.js +173 -0
- package/lib/process.js +228 -0
- package/lib/profile.js +453 -0
- package/lib/python.js +41 -0
- package/lib/race.js +186 -0
- package/lib/registry.js +179 -0
- package/lib/repl.js +135 -0
- package/lib/run.js +403 -0
- package/lib/screenshot.js +152 -0
- package/lib/secrets.js +257 -0
- package/lib/state.js +219 -0
- package/lib/test.js +471 -0
- package/lib/vuln.js +253 -0
- package/lib/watch.js +240 -0
- package/lib/winlog.js +123 -0
- package/package.json +27 -0
- package/skills/ACTIVATE.md +274 -0
- package/skills/README.md +163 -0
- package/skills/agent.md +444 -0
- package/skills/api.md +47 -0
- package/skills/bench.md +117 -0
- package/skills/context.md +116 -0
- package/skills/convention.md +143 -0
- package/skills/coverage.md +99 -0
- package/skills/csharp.md +97 -0
- package/skills/db.md +66 -0
- package/skills/deps-check.md +135 -0
- package/skills/deps.md +143 -0
- package/skills/docker.md +61 -0
- package/skills/dom.md +56 -0
- package/skills/fuzz.md +167 -0
- package/skills/goto.md +111 -0
- package/skills/lint.md +123 -0
- package/skills/port.md +57 -0
- package/skills/profile.md +91 -0
- package/skills/race.md +117 -0
- package/skills/repl.md +81 -0
- package/skills/rules.md +318 -0
- package/skills/run.md +135 -0
- package/skills/secrets.md +170 -0
- package/skills/security.md +360 -0
- package/skills/state.md +261 -0
- package/skills/vuln.md +57 -0
- package/skills/windows.md +320 -0
package/skills/run.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# skill: fchek run — запуск кода и проверка результата
|
|
2
|
+
|
|
3
|
+
## Что делает
|
|
4
|
+
|
|
5
|
+
Запускает файл и возвращает `stdout`, `stderr`, `exit_code` в JSON.
|
|
6
|
+
Это единственный способ **доказать** что код работает.
|
|
7
|
+
Без этого — только предположение.
|
|
8
|
+
|
|
9
|
+
## Поддерживаемые языки
|
|
10
|
+
|
|
11
|
+
| Расширение | Рантайм |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `.py` | python / python3 (авто-определение, игнорирует Windows Store alias) |
|
|
14
|
+
| `.js` `.mjs` | node |
|
|
15
|
+
| `.ts` `.tsx` | ts-node → npx ts-node |
|
|
16
|
+
| `.rs` | cargo run (ищет Cargo.toml вверх) или rustc |
|
|
17
|
+
| `.c` `.cpp` `.cc` | gcc/g++/clang++ → компиляция + запуск |
|
|
18
|
+
| `.go` | go run |
|
|
19
|
+
| `.cs` | **dotnet run** (ищет .csproj/.sln вверх по папкам) |
|
|
20
|
+
| `.sh` | bash / WSL bash (Windows) |
|
|
21
|
+
| `.rb` | ruby |
|
|
22
|
+
| `.php` | php |
|
|
23
|
+
|
|
24
|
+
## Синтаксис
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
fchek run <file> [args...] [--timeout=30000] [--stdin=<text>] [--no-color]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Вывод JSON
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"status": "ok",
|
|
35
|
+
"command": "run",
|
|
36
|
+
"data": {
|
|
37
|
+
"file": "/project/main.py",
|
|
38
|
+
"lang": "python",
|
|
39
|
+
"exit_code": 0,
|
|
40
|
+
"success": true,
|
|
41
|
+
"timed_out": false,
|
|
42
|
+
"stdout": "result: 42\n",
|
|
43
|
+
"stderr": "",
|
|
44
|
+
"stdout_lines": 1,
|
|
45
|
+
"error": null,
|
|
46
|
+
"runner": "python"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
При ошибке компиляции (C/C++/Rust):
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"data": {
|
|
55
|
+
"compiled": false,
|
|
56
|
+
"exit_code": 1,
|
|
57
|
+
"success": false,
|
|
58
|
+
"stderr": "error: expected ';' after expression",
|
|
59
|
+
"error": "Compilation failed — see stderr"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
При таймауте:
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"data": {
|
|
68
|
+
"exit_code": null,
|
|
69
|
+
"success": false,
|
|
70
|
+
"timed_out": true,
|
|
71
|
+
"error": "Timed out after 30000ms — possible infinite loop or blocking I/O"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## КАК AI ДОЛЖЕН ИСПОЛЬЗОВАТЬ ЭТО
|
|
77
|
+
|
|
78
|
+
### ПРАВИЛО — это обязательно, не опционально
|
|
79
|
+
|
|
80
|
+
**Перед тем как написать "готово", "исправил", "работает":**
|
|
81
|
+
```bash
|
|
82
|
+
fchek run <файл>
|
|
83
|
+
```
|
|
84
|
+
Только `"success": true` + `"exit_code": 0` = разрешено говорить "готово".
|
|
85
|
+
|
|
86
|
+
### Алгоритм интерпретации
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
success: true + exit_code: 0 → код работает ✓
|
|
90
|
+
success: false + exit_code: 1 → ошибка в runtime → читай stderr
|
|
91
|
+
success: false + compiled: false → ошибка компиляции → читай stderr
|
|
92
|
+
success: false + timed_out: true → бесконечный цикл или deadlock
|
|
93
|
+
error: "Runtime not found: python3" → fchek doctor → установи рантайм
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Сценарии
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Python
|
|
100
|
+
fchek run main.py
|
|
101
|
+
|
|
102
|
+
# С аргументами
|
|
103
|
+
fchek run main.py arg1 arg2
|
|
104
|
+
|
|
105
|
+
# С stdin
|
|
106
|
+
fchek run parser.py --stdin="test input"
|
|
107
|
+
|
|
108
|
+
# C++ (компилирует + запускает)
|
|
109
|
+
fchek run main.cpp
|
|
110
|
+
|
|
111
|
+
# Rust (ищет Cargo.toml вверх по дереву)
|
|
112
|
+
fchek run src/main.rs
|
|
113
|
+
|
|
114
|
+
# C# (ищет .csproj вверх по дереву, затем dotnet run)
|
|
115
|
+
fchek run Program.cs
|
|
116
|
+
|
|
117
|
+
# Ограничить время (полезно для серверов)
|
|
118
|
+
fchek run server.py --timeout=5000
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### C# — особенности
|
|
122
|
+
|
|
123
|
+
`fchek run` ищет `.csproj` или `.sln` вверх по папкам (до 6 уровней).
|
|
124
|
+
Если не найдено — возвращает ошибку с инструкцией создать проект:
|
|
125
|
+
```
|
|
126
|
+
dotnet new console -o MyApp
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Требует: `dotnet` SDK установлен (`fchek doctor` покажет статус).
|
|
130
|
+
|
|
131
|
+
## Платформа
|
|
132
|
+
|
|
133
|
+
- Windows: C/C++ требует MinGW (`g++`). Если нет — понятная ошибка с ссылкой на установку.
|
|
134
|
+
- Windows: `.sh` файлы запускаются через WSL bash → Git bash fallback.
|
|
135
|
+
- `python3` алиас Microsoft Store игнорируется — используется настоящий Python.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# skill: fchek secrets — поиск секретов в коде
|
|
2
|
+
|
|
3
|
+
## Что делает
|
|
4
|
+
|
|
5
|
+
Сканирует код на наличие захардкоженных секретов: API ключи, пароли, токены, приватные ключи, connection strings.
|
|
6
|
+
Использует gitleaks → trufflehog → встроенные regex (14 паттернов).
|
|
7
|
+
Секреты в выводе **редактируются** — показываются первые 4 символа + хеш.
|
|
8
|
+
|
|
9
|
+
## Синтаксис
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
fchek secrets <path> [--staged] [--full-history]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `--staged` — только staged файлы (pre-commit hook)
|
|
16
|
+
- `--full-history` — вся история git (медленно, но полно)
|
|
17
|
+
|
|
18
|
+
> **C# проекты:** `fchek secrets` — единственная команда fchek которая полностью
|
|
19
|
+
> работает на C#. Папки `bin/`, `obj/` и все `.dll`/`.exe`/`.pdb` файлы
|
|
20
|
+
> **автоматически игнорируются** — шума от .NET артефактов нет.
|
|
21
|
+
> Сканируй `.cs`, `appsettings.json`, `.env`, `*.config` файлы напрямую.
|
|
22
|
+
|
|
23
|
+
## Вывод JSON
|
|
24
|
+
|
|
25
|
+
### Чисто
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"status": "ok",
|
|
29
|
+
"command": "secrets",
|
|
30
|
+
"data": {
|
|
31
|
+
"verdict": "clean",
|
|
32
|
+
"total_findings": 0,
|
|
33
|
+
"summary": { "critical": 0, "high": 0, "other": 0 }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Найдены секреты
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"data": {
|
|
42
|
+
"verdict": "critical",
|
|
43
|
+
"total_findings": 2,
|
|
44
|
+
"summary": { "critical": 1, "high": 1, "other": 0 },
|
|
45
|
+
"findings": [
|
|
46
|
+
{
|
|
47
|
+
"file": "src/config.py",
|
|
48
|
+
"line": 12,
|
|
49
|
+
"pattern": "Stripe Key",
|
|
50
|
+
"severity": "critical",
|
|
51
|
+
"match_redacted": "sk_li***8f2a"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"file": "src/db.py",
|
|
55
|
+
"line": 5,
|
|
56
|
+
"pattern": "Database URL",
|
|
57
|
+
"severity": "high",
|
|
58
|
+
"match_redacted": "post***3b7c"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Паттерны (встроенные)
|
|
66
|
+
|
|
67
|
+
| Паттерн | Severity |
|
|
68
|
+
|---|---|
|
|
69
|
+
| AWS Access Key (AKIA...) | critical |
|
|
70
|
+
| AWS Secret Key | critical |
|
|
71
|
+
| Private Key (-----BEGIN...) | critical |
|
|
72
|
+
| GitHub Token (ghp_...) | critical |
|
|
73
|
+
| Stripe Key (sk_live_...) | critical |
|
|
74
|
+
| Generic API Key | high |
|
|
75
|
+
| Generic Secret/Password | high |
|
|
76
|
+
| Database URL с паролем | high |
|
|
77
|
+
| Slack Token | high |
|
|
78
|
+
| Basic Auth в URL | high |
|
|
79
|
+
| Bearer Token | medium |
|
|
80
|
+
| Hex Secret (32+ chars) | medium |
|
|
81
|
+
|
|
82
|
+
## Как AI должен использовать это
|
|
83
|
+
|
|
84
|
+
### ПРАВИЛО — обязательно перед показом кода с:
|
|
85
|
+
- SQL / ORM / database connections
|
|
86
|
+
- HTTP клиентами с токенами (requests, axios, fetch)
|
|
87
|
+
- Конфигурационными файлами
|
|
88
|
+
- Auth / JWT / OAuth кодом
|
|
89
|
+
- `.env` файлами
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
fchek secrets .
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Алгоритм
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
verdict = "clean" → можно показывать код
|
|
99
|
+
verdict = "issues_found" → убери находки, потом показывай
|
|
100
|
+
verdict = "critical" → СТОП. Критические секреты.
|
|
101
|
+
|
|
102
|
+
ЕСЛИ findings не пустой:
|
|
103
|
+
→ смотри findings[].file и findings[].line
|
|
104
|
+
→ замени значение на os.environ.get("KEY_NAME")
|
|
105
|
+
→ запусти снова → убедись verdict = "clean"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Как правильно исправить
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# ❌ Запрещено — показывать такой код нельзя
|
|
112
|
+
DB_URL = "postgresql://admin:pass123@host:5432/db"
|
|
113
|
+
|
|
114
|
+
# ✅ Правильно
|
|
115
|
+
import os
|
|
116
|
+
DB_URL = os.environ.get("DATABASE_URL")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
// ❌
|
|
121
|
+
const apiKey = "sk_live_AbCdEf123456"
|
|
122
|
+
|
|
123
|
+
// ✅
|
|
124
|
+
const apiKey = process.env.STRIPE_SECRET_KEY
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```rust
|
|
128
|
+
// ❌
|
|
129
|
+
let db_url = "postgresql://admin:pass@host/db";
|
|
130
|
+
|
|
131
|
+
// ✅
|
|
132
|
+
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set");
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```csharp
|
|
136
|
+
// ❌ — C# connection string захардкожен
|
|
137
|
+
var connStr = "Server=prod.db;Database=app;User=admin;Password=Secret123;";
|
|
138
|
+
|
|
139
|
+
// ✅ — из конфига (appsettings.json) или env
|
|
140
|
+
var connStr = configuration.GetConnectionString("DefaultConnection");
|
|
141
|
+
// или
|
|
142
|
+
var connStr = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```csharp
|
|
146
|
+
// ❌ — C# connection string с паролем
|
|
147
|
+
var connStr = "Server=prod.db;Database=app;User=admin;Password=Secret123;";
|
|
148
|
+
|
|
149
|
+
// ✅ — из конфига / environment
|
|
150
|
+
var connStr = configuration.GetConnectionString("DefaultConnection");
|
|
151
|
+
// или
|
|
152
|
+
var connStr = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Инструменты
|
|
156
|
+
|
|
157
|
+
По умолчанию используются встроенные regex (работает без установки).
|
|
158
|
+
Для production-проектов установить gitleaks:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Windows
|
|
162
|
+
winget install gitleaks
|
|
163
|
+
|
|
164
|
+
# Linux/macOS
|
|
165
|
+
brew install gitleaks
|
|
166
|
+
# или
|
|
167
|
+
apt install gitleaks
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`fchek doctor` покажет статус gitleaks и trufflehog.
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# security.md — Безопасность кода
|
|
2
|
+
|
|
3
|
+
Читай этот файл когда пишешь код с авторизацией, базами данных, API, конфигами.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## КОГДА ЗАПУСКАТЬ fchek secrets
|
|
8
|
+
|
|
9
|
+
Запускай `fchek secrets .` **до любого коммита** если задача касается чего-то из таблицы:
|
|
10
|
+
|
|
11
|
+
| Признак | Почему опасно |
|
|
12
|
+
|---|---|
|
|
13
|
+
| SQL запросы или ORM | Connection strings с паролями |
|
|
14
|
+
| HTTP клиенты (requests, axios, fetch) | Bearer tokens, API keys в коде |
|
|
15
|
+
| Конфигурационные файлы (.env, appsettings, config.yml) | Секреты вместо ссылок |
|
|
16
|
+
| Auth, JWT, OAuth | Private keys, client secrets |
|
|
17
|
+
| Переменные окружения в коде | Прямые значения вместо `os.getenv` |
|
|
18
|
+
| Docker / docker-compose | Secrets через ENV напрямую |
|
|
19
|
+
| CI/CD конфиги (.github/workflows, .gitlab-ci) | Tokens в yaml |
|
|
20
|
+
| Новые зависимости с внешним API | Ключи могут попасть в тестах |
|
|
21
|
+
| Любое изменение конфига с `password`, `secret`, `key` | Самоочевидно |
|
|
22
|
+
|
|
23
|
+
**Правило:** сомневаешься — запускай. Занимает 2 секунды.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## КАК ЧИТАТЬ ВЫВОД fchek secrets
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
fchek secrets .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Вывод: `verdict: "clean"` → продолжай
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"status": "ok",
|
|
38
|
+
"data": {
|
|
39
|
+
"verdict": "clean",
|
|
40
|
+
"total_findings": 0,
|
|
41
|
+
"summary": {"critical": 0, "high": 0, "other": 0}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
**Действие:** код безопасен, можно коммитить.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### Вывод: `verdict: "issues_found"` → исправь перед показом
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"data": {
|
|
54
|
+
"verdict": "issues_found",
|
|
55
|
+
"total_findings": 1,
|
|
56
|
+
"findings": [
|
|
57
|
+
{
|
|
58
|
+
"file": "src/config.py",
|
|
59
|
+
"line": 8,
|
|
60
|
+
"pattern": "Generic API Key",
|
|
61
|
+
"severity": "high",
|
|
62
|
+
"match_redacted": "apik***3f2b"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
**Действие:**
|
|
69
|
+
1. Открой `findings[0].file` строку `findings[0].line`
|
|
70
|
+
2. Замени захардкоженное значение на переменную окружения
|
|
71
|
+
3. Запусти `fchek secrets .` снова
|
|
72
|
+
4. Показывай код только после `verdict: "clean"`
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### Вывод: `verdict: "critical"` → СТОП
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"data": {
|
|
81
|
+
"verdict": "critical",
|
|
82
|
+
"total_findings": 2,
|
|
83
|
+
"summary": {"critical": 1, "high": 1, "other": 0},
|
|
84
|
+
"findings": [
|
|
85
|
+
{
|
|
86
|
+
"file": "src/stripe.py",
|
|
87
|
+
"line": 3,
|
|
88
|
+
"pattern": "Stripe Key",
|
|
89
|
+
"severity": "critical",
|
|
90
|
+
"match_redacted": "sk_li***8f2a"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
**Действие:**
|
|
97
|
+
1. СТОП — не показывай, не коммить этот код
|
|
98
|
+
2. Исправь все `severity: "critical"` немедленно
|
|
99
|
+
3. Запусти снова → получи `verdict: "clean"`
|
|
100
|
+
4. Если ключ уже попал в историю git — предупреди пользователя: ключ нужно отозвать
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## ПРИМЕРЫ ИСПРАВЛЕНИЯ ПО ЯЗЫКАМ
|
|
105
|
+
|
|
106
|
+
### Python
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# ❌ Запрещено
|
|
110
|
+
import stripe
|
|
111
|
+
stripe.api_key = "sk_live_AbCdEf123456"
|
|
112
|
+
|
|
113
|
+
DB_URL = "postgresql://admin:SuperSecret123@prod.db:5432/app"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# ✅ Обязательно
|
|
118
|
+
import os
|
|
119
|
+
import stripe
|
|
120
|
+
stripe.api_key = os.environ.get("STRIPE_SECRET_KEY")
|
|
121
|
+
|
|
122
|
+
DB_URL = os.environ.get("DATABASE_URL")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# ❌ Запрещено — даже в тестах
|
|
127
|
+
def test_connection():
|
|
128
|
+
db = connect("postgres://test:password123@localhost/testdb")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# ✅ Обязательно — даже в тестах
|
|
133
|
+
import os
|
|
134
|
+
def test_connection():
|
|
135
|
+
db = connect(os.environ.get("TEST_DATABASE_URL", "postgres://localhost/testdb"))
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### JavaScript / TypeScript
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// ❌ Запрещено
|
|
144
|
+
const apiKey = "sk_live_AbCdEf123456";
|
|
145
|
+
const dbUrl = "mongodb://admin:pass@cluster.mongodb.net/app";
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// ✅ Обязательно
|
|
150
|
+
const apiKey = process.env.STRIPE_SECRET_KEY;
|
|
151
|
+
const dbUrl = process.env.MONGODB_URI;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// ❌ Запрещено
|
|
156
|
+
const config = {
|
|
157
|
+
jwtSecret: "my-super-secret-jwt-key-12345"
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// ✅ Обязательно
|
|
163
|
+
const config = {
|
|
164
|
+
jwtSecret: process.env.JWT_SECRET ?? (() => { throw new Error("JWT_SECRET not set") })()
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### C#
|
|
171
|
+
|
|
172
|
+
```csharp
|
|
173
|
+
// ❌ Запрещено
|
|
174
|
+
var connStr = "Server=prod.db;Database=app;User=admin;Password=Secret123;";
|
|
175
|
+
var apiKey = "sk_live_AbCdEf123456";
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```csharp
|
|
179
|
+
// ✅ Обязательно — из конфига (appsettings.json)
|
|
180
|
+
var connStr = _configuration.GetConnectionString("DefaultConnection");
|
|
181
|
+
|
|
182
|
+
// ✅ Или из переменных окружения
|
|
183
|
+
var connStr = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
|
|
184
|
+
var apiKey = Environment.GetEnvironmentVariable("STRIPE_SECRET_KEY");
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```csharp
|
|
188
|
+
// ✅ С использованием IOptions<T>
|
|
189
|
+
public class StripeOptions
|
|
190
|
+
{
|
|
191
|
+
public string SecretKey { get; set; } = string.Empty;
|
|
192
|
+
}
|
|
193
|
+
// appsettings.json: { "Stripe": { "SecretKey": "" } } ← пустое, в env vars
|
|
194
|
+
// docker / deploy: STRIPE__SECRETKEY=sk_live_...
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### Rust
|
|
200
|
+
|
|
201
|
+
```rust
|
|
202
|
+
// ❌ Запрещено
|
|
203
|
+
let db_url = "postgresql://admin:pass@host/db";
|
|
204
|
+
let api_key = "sk_live_AbCdEf123456";
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```rust
|
|
208
|
+
// ✅ Обязательно
|
|
209
|
+
let db_url = std::env::var("DATABASE_URL")
|
|
210
|
+
.expect("DATABASE_URL must be set");
|
|
211
|
+
let api_key = std::env::var("STRIPE_SECRET_KEY")
|
|
212
|
+
.expect("STRIPE_SECRET_KEY must be set");
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```rust
|
|
216
|
+
// ✅ С дефолтом для разработки (только non-production)
|
|
217
|
+
#[cfg(not(feature = "production"))]
|
|
218
|
+
let db_url = std::env::var("DATABASE_URL")
|
|
219
|
+
.unwrap_or_else(|_| "postgresql://localhost/dev".to_string());
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## SQL INJECTION — параметризованные запросы
|
|
225
|
+
|
|
226
|
+
Никогда не собирай SQL строку конкатенацией или f-strings.
|
|
227
|
+
|
|
228
|
+
### Python
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
# ❌ SQL injection — никогда
|
|
232
|
+
user_id = request.args.get("id")
|
|
233
|
+
query = f"SELECT * FROM users WHERE id = {user_id}"
|
|
234
|
+
cursor.execute(query)
|
|
235
|
+
|
|
236
|
+
# ❌ Тоже запрещено
|
|
237
|
+
query = "SELECT * FROM users WHERE name = '" + name + "'"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# ✅ Параметризованный запрос
|
|
242
|
+
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
|
243
|
+
|
|
244
|
+
# ✅ SQLAlchemy
|
|
245
|
+
result = session.execute(
|
|
246
|
+
select(User).where(User.id == user_id)
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### JavaScript
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
// ❌ SQL injection
|
|
254
|
+
const query = `SELECT * FROM users WHERE id = ${userId}`;
|
|
255
|
+
db.query(query);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
// ✅ node-postgres
|
|
260
|
+
db.query('SELECT * FROM users WHERE id = $1', [userId]);
|
|
261
|
+
|
|
262
|
+
// ✅ Prisma (ORM — безопасен по умолчанию)
|
|
263
|
+
const user = await prisma.user.findUnique({ where: { id: userId } });
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### C#
|
|
267
|
+
|
|
268
|
+
```csharp
|
|
269
|
+
// ❌ SQL injection
|
|
270
|
+
var query = $"SELECT * FROM Users WHERE Id = {userId}";
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```csharp
|
|
274
|
+
// ✅ Dapper
|
|
275
|
+
var user = await db.QueryFirstOrDefaultAsync<User>(
|
|
276
|
+
"SELECT * FROM Users WHERE Id = @Id",
|
|
277
|
+
new { Id = userId }
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// ✅ EF Core (LINQ — безопасен по умолчанию)
|
|
281
|
+
var user = await context.Users.FindAsync(userId);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Rust
|
|
285
|
+
|
|
286
|
+
```rust
|
|
287
|
+
// ❌ SQL injection
|
|
288
|
+
let query = format!("SELECT * FROM users WHERE id = {}", user_id);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
```rust
|
|
292
|
+
// ✅ sqlx
|
|
293
|
+
let user = sqlx::query_as::<_, User>(
|
|
294
|
+
"SELECT * FROM users WHERE id = ?"
|
|
295
|
+
)
|
|
296
|
+
.bind(user_id)
|
|
297
|
+
.fetch_one(&pool)
|
|
298
|
+
.await?;
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## ЧТО НЕ ЛОГИРОВАТЬ
|
|
304
|
+
|
|
305
|
+
Запрещено передавать в любые `log`, `print`, `console.log`, `Logger.Log`, `tracing::info!`:
|
|
306
|
+
|
|
307
|
+
| Запрещено логировать | Причина |
|
|
308
|
+
|---|---|
|
|
309
|
+
| Пароли и хеши паролей | Утечка в файлы логов |
|
|
310
|
+
| API ключи и токены | Rotated токены в логах — уязвимость |
|
|
311
|
+
| JWT токены (полностью) | Payload декодируется — данные пользователя |
|
|
312
|
+
| Cookie значения (session_id) | Hijacking через логи |
|
|
313
|
+
| Credit card номера | PCI DSS нарушение |
|
|
314
|
+
| SSN / паспортные данные | GDPR / HIPAA нарушение |
|
|
315
|
+
| Private keys | Самоочевидно |
|
|
316
|
+
| Connection strings | Пароль в строке |
|
|
317
|
+
| Request headers: Authorization | Bearer token |
|
|
318
|
+
|
|
319
|
+
### Примеры:
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# ❌ Запрещено
|
|
323
|
+
logger.info(f"User logged in with token: {jwt_token}")
|
|
324
|
+
logger.debug(f"DB connection: {db_url}")
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
# ✅ Правильно
|
|
329
|
+
logger.info(f"User {user.id} logged in")
|
|
330
|
+
logger.debug(f"DB connection established (host: {db_host})")
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
// ❌ Запрещено
|
|
335
|
+
console.log('Auth header:', req.headers.authorization);
|
|
336
|
+
|
|
337
|
+
// ✅ Правильно
|
|
338
|
+
console.log('Auth header present:', !!req.headers.authorization);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
```csharp
|
|
342
|
+
// ❌ Запрещено
|
|
343
|
+
_logger.LogDebug("Connection string: {ConnectionString}", connStr);
|
|
344
|
+
|
|
345
|
+
// ✅ Правильно
|
|
346
|
+
_logger.LogDebug("Database connected to host: {Host}", dbHost);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Чеклист перед коммитом с auth/db кодом
|
|
352
|
+
|
|
353
|
+
Проверь каждый пункт:
|
|
354
|
+
|
|
355
|
+
- [ ] `fchek secrets .` → `verdict: "clean"`
|
|
356
|
+
- [ ] Connection strings читаются из `os.environ` / `process.env` / `std::env::var` / `GetConnectionString`
|
|
357
|
+
- [ ] `.env` файл в `.gitignore`
|
|
358
|
+
- [ ] Нет захардкоженных секретов в тестах
|
|
359
|
+
- [ ] SQL запросы используют параметры, не конкатенацию строк
|
|
360
|
+
- [ ] Ничего чувствительного не передаётся в логи
|