security: harden secret hygiene and pre-commit scanning
This commit is contained in:
5
.githooks/pre-commit
Executable file
5
.githooks/pre-commit
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo_root="$(git rev-parse --show-toplevel)"
|
||||||
|
"$repo_root/scripts/check-secrets.sh"
|
||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,5 +1,16 @@
|
|||||||
# QuoteForge
|
# QuoteForge
|
||||||
config.yaml
|
config.yaml
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
*.crt
|
||||||
|
id_rsa
|
||||||
|
id_rsa.*
|
||||||
|
secrets.yaml
|
||||||
|
secrets.yml
|
||||||
|
|
||||||
# Local SQLite database (contains encrypted credentials)
|
# Local SQLite database (contains encrypted credentials)
|
||||||
/data/*.db
|
/data/*.db
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: build build-release clean test run version
|
.PHONY: build build-release clean test run version install-hooks
|
||||||
|
|
||||||
# Get version from git
|
# Get version from git
|
||||||
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||||||
@@ -72,6 +72,12 @@ deps:
|
|||||||
go mod download
|
go mod download
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
|
# Install local git hooks
|
||||||
|
install-hooks:
|
||||||
|
git config core.hooksPath .githooks
|
||||||
|
chmod +x .githooks/pre-commit scripts/check-secrets.sh
|
||||||
|
@echo "Installed git hooks from .githooks/"
|
||||||
|
|
||||||
# Help
|
# Help
|
||||||
help:
|
help:
|
||||||
@echo "QuoteForge Server (qfs) - Build Commands"
|
@echo "QuoteForge Server (qfs) - Build Commands"
|
||||||
@@ -92,6 +98,7 @@ help:
|
|||||||
@echo " run Run development server"
|
@echo " run Run development server"
|
||||||
@echo " watch Run with auto-restart (requires entr)"
|
@echo " watch Run with auto-restart (requires entr)"
|
||||||
@echo " deps Install/update dependencies"
|
@echo " deps Install/update dependencies"
|
||||||
|
@echo " install-hooks Install local git hooks (secret scan on commit)"
|
||||||
@echo " help Show this help"
|
@echo " help Show this help"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Current version: $(VERSION)"
|
@echo "Current version: $(VERSION)"
|
||||||
|
|||||||
@@ -111,10 +111,10 @@ go run ./cmd/migrate_ops_projects -apply -yes
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- 1) Создать пользователя (если его ещё нет)
|
-- 1) Создать пользователя (если его ещё нет)
|
||||||
CREATE USER IF NOT EXISTS 'quote_user'@'%' IDENTIFIED BY 'DB_PASSWORD_PLACEHOLDER';
|
CREATE USER IF NOT EXISTS 'quote_user'@'%' IDENTIFIED BY '<DB_PASSWORD>';
|
||||||
|
|
||||||
-- 2) Если пользователь уже существовал, принудительно обновить пароль
|
-- 2) Если пользователь уже существовал, принудительно обновить пароль
|
||||||
ALTER USER 'quote_user'@'%' IDENTIFIED BY 'DB_PASSWORD_PLACEHOLDER';
|
ALTER USER 'quote_user'@'%' IDENTIFIED BY '<DB_PASSWORD>';
|
||||||
|
|
||||||
-- 3) (Опционально, но рекомендуется) удалить дубли пользователя с другими host,
|
-- 3) (Опционально, но рекомендуется) удалить дубли пользователя с другими host,
|
||||||
-- чтобы не возникало конфликтов вида user@localhost vs user@'%'
|
-- чтобы не возникало конфликтов вида user@localhost vs user@'%'
|
||||||
@@ -144,7 +144,7 @@ SHOW CREATE USER 'quote_user'@'%';
|
|||||||
Полный набор прав для пользователя квотаций:
|
Полный набор прав для пользователя квотаций:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
GRANT USAGE ON *.* TO 'quote_user'@'%' IDENTIFIED BY 'DB_PASSWORD_PLACEHOLDER';
|
GRANT USAGE ON *.* TO 'quote_user'@'%' IDENTIFIED BY '<DB_PASSWORD>';
|
||||||
GRANT SELECT ON RFQ_LOG.lot TO 'quote_user'@'%';
|
GRANT SELECT ON RFQ_LOG.lot TO 'quote_user'@'%';
|
||||||
GRANT SELECT ON RFQ_LOG.qt_lot_metadata TO 'quote_user'@'%';
|
GRANT SELECT ON RFQ_LOG.qt_lot_metadata TO 'quote_user'@'%';
|
||||||
GRANT SELECT ON RFQ_LOG.qt_categories TO 'quote_user'@'%';
|
GRANT SELECT ON RFQ_LOG.qt_categories TO 'quote_user'@'%';
|
||||||
@@ -187,6 +187,7 @@ make build-all # Сборка для всех платформ (Linux, mac
|
|||||||
make build-windows # Только для Windows
|
make build-windows # Только для Windows
|
||||||
make run # Запуск dev сервера
|
make run # Запуск dev сервера
|
||||||
make test # Запуск тестов
|
make test # Запуск тестов
|
||||||
|
make install-hooks # Установить git hooks (блокировка коммита с секретами)
|
||||||
make clean # Очистка bin/
|
make clean # Очистка bin/
|
||||||
make help # Показать все команды
|
make help # Показать все команды
|
||||||
```
|
```
|
||||||
|
|||||||
56
scripts/check-secrets.sh
Executable file
56
scripts/check-secrets.sh
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if ! git rev-parse --git-dir >/dev/null 2>&1; then
|
||||||
|
echo "Not inside a git repository."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v rg >/dev/null 2>&1; then
|
||||||
|
echo "ripgrep (rg) is required for secret scanning."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
staged_files=()
|
||||||
|
while IFS= read -r file; do
|
||||||
|
staged_files+=("$file")
|
||||||
|
done < <(git diff --cached --name-only --diff-filter=ACMRTUXB)
|
||||||
|
|
||||||
|
if [ "${#staged_files[@]}" -eq 0 ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
secret_pattern='AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{35}|-----BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY-----|(?i)(password|passwd|pwd|secret|token|api[_-]?key|jwt_secret)\s*[:=]\s*["'"'"'][^"'"'"'\s]{8,}["'"'"']'
|
||||||
|
allow_pattern='CHANGE_ME|REDACTED|PLACEHOLDER|EXAMPLE|example|<[^>]+>'
|
||||||
|
|
||||||
|
found=0
|
||||||
|
|
||||||
|
for file in "${staged_files[@]}"; do
|
||||||
|
case "$file" in
|
||||||
|
dist/*|*.png|*.jpg|*.jpeg|*.gif|*.webp|*.pdf|*.zip|*.gz|*.exe|*.dll|*.so|*.dylib)
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if ! content="$(git show ":$file" 2>/dev/null)"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
hits="$(printf '%s' "$content" | rg -n --no-heading -e "$secret_pattern" || true)"
|
||||||
|
if [ -n "$hits" ]; then
|
||||||
|
filtered="$(printf '%s\n' "$hits" | rg -v -e "$allow_pattern" || true)"
|
||||||
|
if [ -n "$filtered" ]; then
|
||||||
|
echo "Potential secret found in staged file: $file"
|
||||||
|
printf '%s\n' "$filtered"
|
||||||
|
found=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$found" -ne 0 ]; then
|
||||||
|
echo
|
||||||
|
echo "Commit blocked: remove or redact secrets before committing."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user