@vnptnan/gate 0.0.0-stage → 1.0.1
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/LICENSE +21 -0
- package/README.md +51 -2
- package/VERSION +1 -0
- package/bin/cli.js +226 -0
- package/ci/.gitlab-ci.yml +41 -0
- package/cli/vnptnan-gate +90 -0
- package/cli/vnptnan-gate.cmd +9 -0
- package/engine/gate-deps.sh +110 -0
- package/engine/gate-lib.sh +285 -0
- package/engine/gate-scan.sh +155 -0
- package/engine/prompt-check.sh +77 -0
- package/gate.conf +13 -0
- package/hooks/commit-msg +10 -0
- package/hooks/pre-commit +7 -0
- package/hooks/pre-push +7 -0
- package/installer/install-server.sh +33 -0
- package/installer/install.ps1 +80 -0
- package/installer/uninstall.ps1 +15 -0
- package/package.json +44 -3
- package/policy.json +4 -0
- package/rules/ai-tools.rules +15 -0
- package/rules/gate.rules +75 -0
- package/rules/packages.rules +18 -0
- package/server/pre-receive +13 -0
- package/tools/lint-rules.sh +37 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Thư viện dùng chung của Cổng kiểm soát ATBMTT (VNPTNAN_GATE).
|
|
3
|
+
# Chạy được trên Git Bash (Windows), Linux (máy chủ GitLab) và macOS. Chỉ cần: bash >= 4, git, grep -E, awk, sed, wc, stat.
|
|
4
|
+
# Không có đầu vào/đầu ra riêng: gate-scan.sh và các hook gọi các hàm ở đây.
|
|
5
|
+
|
|
6
|
+
# ---------- Vị trí và cấu hình ----------
|
|
7
|
+
gate_root() {
|
|
8
|
+
local here
|
|
9
|
+
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
10
|
+
echo "${GATE_ROOT:-$(dirname "$here")}"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
GATE_ROOT=$(gate_root)
|
|
14
|
+
GATE_VERSION=$(cat "$GATE_ROOT/VERSION" 2>/dev/null || echo "0.0.0")
|
|
15
|
+
GATE_PREFIX="[VNPTNAN_GATE]"
|
|
16
|
+
|
|
17
|
+
# Giá trị mặc định; ghi đè bằng gate.conf ở gốc bộ công cụ, rồi .vnptnan-gate/gate.conf của dự án, rồi biến môi trường GATE_*.
|
|
18
|
+
GATE_MODE=${GATE_MODE:-} # enforce | audit
|
|
19
|
+
GATE_DEPS=${GATE_DEPS:-} # block | warn | off
|
|
20
|
+
GATE_DEPS_AUDIT=${GATE_DEPS_AUDIT:-} # 1 = chạy npm audit / dotnet list --vulnerable (cần mạng, chậm)
|
|
21
|
+
GATE_MAX_COMMITS=${GATE_MAX_COMMITS:-}
|
|
22
|
+
GATE_MAX_FILE=${GATE_MAX_FILE:-} # bytes; file lớn hơn không quét theo dòng
|
|
23
|
+
GATE_SKIP_PATHS=${GATE_SKIP_PATHS:-}
|
|
24
|
+
GATE_SKIP_EXTRA=${GATE_SKIP_EXTRA:-} # regex thêm vào SKIP_PATHS mặc định (khoá SKIP_PATHS_EXTRA trong gate.conf)
|
|
25
|
+
GATE_SERVER=${GATE_SERVER:-0} # 1 khi chạy trong pre-receive của GitLab
|
|
26
|
+
GATE_LOG=${GATE_LOG:-}
|
|
27
|
+
GATE_QUIET=${GATE_QUIET:-0}
|
|
28
|
+
|
|
29
|
+
gate_load_conf_file() {
|
|
30
|
+
local f=$1 key val
|
|
31
|
+
[ -f "$f" ] || return 0
|
|
32
|
+
while IFS='=' read -r key val || [ -n "$key" ]; do
|
|
33
|
+
key=$(printf '%s' "$key" | tr -d ' \r')
|
|
34
|
+
val=$(printf '%s' "$val" | sed 's/[[:space:]]*$//; s/\r$//')
|
|
35
|
+
case "$key" in
|
|
36
|
+
''|'#'*) continue ;;
|
|
37
|
+
MODE) [ -z "$GATE_MODE" ] && GATE_MODE=$val ;;
|
|
38
|
+
DEPS) [ -z "$GATE_DEPS" ] && GATE_DEPS=$val ;;
|
|
39
|
+
DEPS_AUDIT) [ -z "$GATE_DEPS_AUDIT" ] && GATE_DEPS_AUDIT=$val ;;
|
|
40
|
+
MAX_COMMITS) [ -z "$GATE_MAX_COMMITS" ] && GATE_MAX_COMMITS=$val ;;
|
|
41
|
+
MAX_FILE) [ -z "$GATE_MAX_FILE" ] && GATE_MAX_FILE=$val ;;
|
|
42
|
+
SKIP_PATHS) [ -z "$GATE_SKIP_PATHS" ] && GATE_SKIP_PATHS=$val ;;
|
|
43
|
+
SKIP_PATHS_EXTRA) [ -z "$GATE_SKIP_EXTRA" ] && GATE_SKIP_EXTRA=$val ;;
|
|
44
|
+
LOG) [ -z "$GATE_LOG" ] && GATE_LOG=$val ;;
|
|
45
|
+
esac
|
|
46
|
+
done < "$f"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# Thứ tự ưu tiên: biến môi trường > .vnptnan-gate/gate.conf của dự án > gate.conf của bộ công cụ > mặc định.
|
|
50
|
+
gate_load_conf() {
|
|
51
|
+
local project_root=${1:-}
|
|
52
|
+
[ -n "$project_root" ] && gate_load_conf_file "$project_root/.vnptnan-gate/gate.conf"
|
|
53
|
+
gate_load_conf_file "$GATE_ROOT/gate.conf"
|
|
54
|
+
: "${GATE_MODE:=enforce}"
|
|
55
|
+
: "${GATE_DEPS:=warn}"
|
|
56
|
+
: "${GATE_DEPS_AUDIT:=0}"
|
|
57
|
+
: "${GATE_MAX_COMMITS:=500}"
|
|
58
|
+
: "${GATE_MAX_FILE:=5242880}"
|
|
59
|
+
: "${GATE_SKIP_PATHS:=(^|/)(node_modules|bin|obj|dist|build|out|packages|\.git|vendor|coverage|TestResults)/|\.(min\.js|min\.css|map|lock|snap)$|(^|/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|packages\.lock\.json)$|(^|/)\.vnptnan-gate/}"
|
|
60
|
+
[ -n "$GATE_SKIP_EXTRA" ] && GATE_SKIP_PATHS="$GATE_SKIP_PATHS|$GATE_SKIP_EXTRA"
|
|
61
|
+
if [ -z "$GATE_LOG" ]; then
|
|
62
|
+
if [ "$GATE_SERVER" = "1" ]; then GATE_LOG=/var/log/vnptnan-gate/gate.log
|
|
63
|
+
elif [ -n "${LOCALAPPDATA:-}" ]; then GATE_LOG="$LOCALAPPDATA/VNPTNAN_GATE/gate.log"
|
|
64
|
+
else GATE_LOG="$HOME/.vnptnan-gate/gate.log"; fi
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# ---------- Thông báo ----------
|
|
69
|
+
gate_say() {
|
|
70
|
+
# Mọi dòng ra stderr. Trong pre-receive, GitLab chỉ hiện dòng có tiền tố GL-HOOK-ERR:.
|
|
71
|
+
local line
|
|
72
|
+
for line in "$@"; do
|
|
73
|
+
if [ "$GATE_SERVER" = "1" ]; then printf 'GL-HOOK-ERR: %s %s\n' "$GATE_PREFIX" "$line" >&2
|
|
74
|
+
else printf '%s %s\n' "$GATE_PREFIX" "$line" >&2; fi
|
|
75
|
+
done
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# ---------- Nạp rule ----------
|
|
79
|
+
# Mảng song song, chỉ số i: RULE_ID RULE_LEVEL RULE_KIND RULE_RE RULE_MSG
|
|
80
|
+
RULE_ID=(); RULE_LEVEL=(); RULE_KIND=(); RULE_RE=(); RULE_MSG=()
|
|
81
|
+
TEXT_BLOCK=''; TEXT_WARN=''; TEXT_AUDIT=''
|
|
82
|
+
# Ngoại lệ của dự án: ALLOW_ID ALLOW_PATHRE (từ .vnptnan-gate/allow.rules)
|
|
83
|
+
ALLOW_ID=(); ALLOW_PATHRE=()
|
|
84
|
+
|
|
85
|
+
gate_load_rules() {
|
|
86
|
+
local file=${1:-$GATE_ROOT/rules/gate.rules} line id level kind re msg
|
|
87
|
+
[ -f "$file" ] || { gate_say "Không tìm thấy file rule: $file"; return 2; }
|
|
88
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
89
|
+
line=${line%$'\r'}
|
|
90
|
+
[ -z "$line" ] && continue
|
|
91
|
+
case "$line" in
|
|
92
|
+
'#'*) continue ;;
|
|
93
|
+
'@block-suffix'*) TEXT_BLOCK=${line#*$'\t'} ; continue ;;
|
|
94
|
+
'@warn-suffix'*) TEXT_WARN=${line#*$'\t'} ; continue ;;
|
|
95
|
+
'@audit-suffix'*) TEXT_AUDIT=${line#*$'\t'} ; continue ;;
|
|
96
|
+
'@'*) continue ;;
|
|
97
|
+
esac
|
|
98
|
+
IFS=$'\t' read -r id level kind re msg <<< "$line"
|
|
99
|
+
if [ -z "$id" ] || [ -z "$level" ] || [ -z "$kind" ] || [ -z "$re" ]; then
|
|
100
|
+
gate_say "Dòng rule hỏng (cần 5 cột ngăn bằng TAB): $line"; return 2
|
|
101
|
+
fi
|
|
102
|
+
case "$level" in block|warn) ;; *) gate_say "Mức không hợp lệ '$level' ở rule $id"; return 2 ;; esac
|
|
103
|
+
case "$kind" in line|path|count|size|allow|exempt) ;; *) gate_say "Loại không hợp lệ '$kind' ở rule $id"; return 2 ;; esac
|
|
104
|
+
RULE_ID+=("$id"); RULE_LEVEL+=("$level"); RULE_KIND+=("$kind"); RULE_RE+=("$re"); RULE_MSG+=("${msg:-}")
|
|
105
|
+
done < "$file"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
gate_load_allow() {
|
|
109
|
+
# .vnptnan-gate/allow.rules trong repo: rule-id regex-đường-dẫn người-duyệt lý-do
|
|
110
|
+
local file=$1 line id pathre
|
|
111
|
+
[ -f "$file" ] || return 0
|
|
112
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
113
|
+
line=${line%$'\r'}
|
|
114
|
+
[ -z "$line" ] && continue
|
|
115
|
+
case "$line" in '#'*) continue ;; esac
|
|
116
|
+
IFS=$'\t' read -r id pathre _ <<< "$line"
|
|
117
|
+
[ -n "$id" ] && [ -n "$pathre" ] || continue
|
|
118
|
+
ALLOW_ID+=("$id"); ALLOW_PATHRE+=("$pathre")
|
|
119
|
+
done < "$file"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
gate_is_allowed() {
|
|
123
|
+
# $1 rule id, $2 path. Đúng khi dự án đã khai ngoại lệ cho rule đó trên đường dẫn đó.
|
|
124
|
+
local i
|
|
125
|
+
for i in "${!ALLOW_ID[@]}"; do
|
|
126
|
+
if [ "${ALLOW_ID[$i]}" = "$1" ] && printf '%s' "$2" | grep -qiE -- "${ALLOW_PATHRE[$i]}"; then return 0; fi
|
|
127
|
+
done
|
|
128
|
+
return 1
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
gate_path_skipped() { printf '%s' "$1" | grep -qiE -- "$GATE_SKIP_PATHS"; }
|
|
132
|
+
|
|
133
|
+
gate_path_exempt() {
|
|
134
|
+
local i
|
|
135
|
+
for i in "${!RULE_ID[@]}"; do
|
|
136
|
+
[ "${RULE_KIND[$i]}" = exempt ] || continue
|
|
137
|
+
printf '%s' "$1" | grep -qiE -- "${RULE_RE[$i]}" && return 0
|
|
138
|
+
done
|
|
139
|
+
return 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
gate_line_allowed() {
|
|
143
|
+
# $1 nội dung dòng, $2 id rule đang xét. Đúng khi dòng là giá trị mẫu hoặc có gate-allow:<id>.
|
|
144
|
+
local i
|
|
145
|
+
# Ngoại lệ tại dòng chỉ dành cho mã nguồn (có review qua MR); prompt-check.sh tắt bằng GATE_NO_INLINE_ALLOW=1
|
|
146
|
+
if [ "${GATE_NO_INLINE_ALLOW:-0}" != 1 ]; then
|
|
147
|
+
printf '%s' "$1" | grep -qiE -- "gate-allow:$2([^a-z0-9-]|$)" && return 0
|
|
148
|
+
fi
|
|
149
|
+
for i in "${!RULE_ID[@]}"; do
|
|
150
|
+
[ "${RULE_KIND[$i]}" = allow ] || continue
|
|
151
|
+
printf '%s' "$1" | grep -qiE -- "${RULE_RE[$i]}" && return 0
|
|
152
|
+
done
|
|
153
|
+
return 1
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# ---------- Che giá trị nhạy cảm khi in và ghi log ----------
|
|
157
|
+
gate_mask() {
|
|
158
|
+
# In tối đa 72 ký tự của dòng, phần khớp regex được thay bằng 6 ký tự đầu + ***.
|
|
159
|
+
local text=$1 re=$2 m masked
|
|
160
|
+
m=$(printf '%s' "$text" | grep -oiE -- "$re" | head -n1)
|
|
161
|
+
if [ -n "$m" ] && [ ${#m} -gt 8 ]; then
|
|
162
|
+
masked=${text//"$m"/${m:0:6}***}
|
|
163
|
+
else
|
|
164
|
+
masked=$text
|
|
165
|
+
fi
|
|
166
|
+
masked=$(printf '%s' "$masked" | sed 's/^[[:space:]]*//')
|
|
167
|
+
[ ${#masked} -gt 72 ] && masked="${masked:0:72}..."
|
|
168
|
+
printf '%s' "$masked"
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# ---------- Ghi log ----------
|
|
172
|
+
gate_log() {
|
|
173
|
+
# stage level id decision path lineno masked
|
|
174
|
+
local dir
|
|
175
|
+
dir=$(dirname "$GATE_LOG")
|
|
176
|
+
mkdir -p "$dir" 2>/dev/null || return 0
|
|
177
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
|
178
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$GATE_VERSION" "$GATE_MODE" "$1" "$2" "$3" "$4" "${GATE_REPO_NAME:-?}" "$5" "$6" "$7" >> "$GATE_LOG" 2>/dev/null || true
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# ---------- Kết quả ----------
|
|
182
|
+
GATE_N_BLOCK=0; GATE_N_WARN=0; GATE_N_ALLOWED=0
|
|
183
|
+
GATE_STAGE=${GATE_STAGE:-scan}
|
|
184
|
+
GATE_REPO_NAME=${GATE_REPO_NAME:-}
|
|
185
|
+
|
|
186
|
+
gate_report_finding() {
|
|
187
|
+
# $1 level, $2 id, $3 path, $4 lineno, $5 masked text, $6 message
|
|
188
|
+
local level=$1 id=$2 path=$3 ln=$4 masked=$5 msg=$6 decision tag
|
|
189
|
+
if gate_is_allowed "$id" "$path"; then
|
|
190
|
+
GATE_N_ALLOWED=$((GATE_N_ALLOWED + 1))
|
|
191
|
+
gate_log "$GATE_STAGE" "$level" "$id" allowed "${GATE_PATH_PREFIX:-}$path" "$ln" "$masked"
|
|
192
|
+
return 0
|
|
193
|
+
fi
|
|
194
|
+
if [ "$level" = block ] && [ "$GATE_MODE" != audit ]; then
|
|
195
|
+
GATE_N_BLOCK=$((GATE_N_BLOCK + 1)); decision=block; tag=CHAN
|
|
196
|
+
elif [ "$level" = block ]; then
|
|
197
|
+
GATE_N_WARN=$((GATE_N_WARN + 1)); decision=audit; tag=AUDIT
|
|
198
|
+
else
|
|
199
|
+
GATE_N_WARN=$((GATE_N_WARN + 1)); decision=warn; tag=CANH-BAO
|
|
200
|
+
fi
|
|
201
|
+
gate_log "$GATE_STAGE" "$level" "$id" "$decision" "${GATE_PATH_PREFIX:-}$path" "$ln" "$masked"
|
|
202
|
+
if [ -n "$ln" ]; then gate_say "$tag $id ${GATE_PATH_PREFIX:-}$path:$ln $msg"
|
|
203
|
+
else gate_say "$tag $id ${GATE_PATH_PREFIX:-}$path $msg"; fi
|
|
204
|
+
[ -n "$masked" ] && [ "$GATE_QUIET" != 1 ] && gate_say " > $masked"
|
|
205
|
+
return 0
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# ---------- Quét một file ----------
|
|
209
|
+
# gate_scan_file <path> <file-chứa-các-dòng-cần-quét> [file-đầy-đủ] [kích-thước-bytes]
|
|
210
|
+
# file-chứa-các-dòng: các dòng được thêm (diff) hoặc toàn bộ nội dung (quét cả cây); đã bỏ dấu + ở đầu.
|
|
211
|
+
# file-đầy-đủ: nội dung đầy đủ để kiểm rule count/size; bỏ trống thì dùng file-chứa-các-dòng.
|
|
212
|
+
gate_scan_file() {
|
|
213
|
+
local path=$1 lines=$2 full=${3:-$2} size=${4:-} i id level kind re msg hit ln text masked n thr
|
|
214
|
+
gate_path_skipped "$path" && return 0
|
|
215
|
+
|
|
216
|
+
# Rule đường dẫn
|
|
217
|
+
if ! gate_path_exempt "$path"; then
|
|
218
|
+
for i in "${!RULE_ID[@]}"; do
|
|
219
|
+
[ "${RULE_KIND[$i]}" = path ] || continue
|
|
220
|
+
if printf '%s' "$path" | grep -qiE -- "${RULE_RE[$i]}"; then
|
|
221
|
+
gate_report_finding "${RULE_LEVEL[$i]}" "${RULE_ID[$i]}" "$path" "" "" "${RULE_MSG[$i]}"
|
|
222
|
+
fi
|
|
223
|
+
done
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
[ -s "$lines" ] || return 0
|
|
227
|
+
# File nhị phân: không quét theo dòng.
|
|
228
|
+
if grep -qI . "$lines" 2>/dev/null; then :; else return 0; fi
|
|
229
|
+
if [ -z "$size" ]; then size=$(wc -c < "$full" | tr -d ' '); fi
|
|
230
|
+
|
|
231
|
+
# Rule kích thước
|
|
232
|
+
for i in "${!RULE_ID[@]}"; do
|
|
233
|
+
[ "${RULE_KIND[$i]}" = size ] || continue
|
|
234
|
+
thr=${RULE_RE[$i]%%|*}; re=${RULE_RE[$i]#*|}
|
|
235
|
+
if [ "$size" -gt "$thr" ] && printf '%s' "$path" | grep -qiE -- "$re"; then
|
|
236
|
+
gate_report_finding "${RULE_LEVEL[$i]}" "${RULE_ID[$i]}" "$path" "" "$size bytes" "${RULE_MSG[$i]}"
|
|
237
|
+
fi
|
|
238
|
+
done
|
|
239
|
+
|
|
240
|
+
[ "$size" -gt "$GATE_MAX_FILE" ] && return 0
|
|
241
|
+
|
|
242
|
+
# Rule đếm (trên nội dung đầy đủ)
|
|
243
|
+
for i in "${!RULE_ID[@]}"; do
|
|
244
|
+
[ "${RULE_KIND[$i]}" = count ] || continue
|
|
245
|
+
thr=${RULE_RE[$i]%%|*}; re=${RULE_RE[$i]#*|}
|
|
246
|
+
n=$(grep -ciE -- "$re" "$full" 2>/dev/null || true); n=${n:-0}
|
|
247
|
+
if [ "$n" -ge "$thr" ]; then
|
|
248
|
+
gate_report_finding "${RULE_LEVEL[$i]}" "${RULE_ID[$i]}" "$path" "" "$n dòng khớp (ngưỡng $thr)" "${RULE_MSG[$i]}"
|
|
249
|
+
fi
|
|
250
|
+
done
|
|
251
|
+
|
|
252
|
+
# Rule theo dòng (trên các dòng được thêm)
|
|
253
|
+
for i in "${!RULE_ID[@]}"; do
|
|
254
|
+
[ "${RULE_KIND[$i]}" = line ] || continue
|
|
255
|
+
id=${RULE_ID[$i]}; level=${RULE_LEVEL[$i]}; re=${RULE_RE[$i]}; msg=${RULE_MSG[$i]}
|
|
256
|
+
while IFS= read -r hit; do
|
|
257
|
+
[ -n "$hit" ] || continue
|
|
258
|
+
ln=${hit%%:*}; text=${hit#*:}
|
|
259
|
+
if gate_line_allowed "$text" "$id"; then
|
|
260
|
+
GATE_N_ALLOWED=$((GATE_N_ALLOWED + 1))
|
|
261
|
+
gate_log "$GATE_STAGE" "$level" "$id" allowed-inline "$path" "$ln" "$(gate_mask "$text" "$re")"
|
|
262
|
+
continue
|
|
263
|
+
fi
|
|
264
|
+
masked=$(gate_mask "$text" "$re")
|
|
265
|
+
gate_report_finding "$level" "$id" "$path" "$ln" "$masked" "$msg"
|
|
266
|
+
done < <(grep -niE -- "$re" "$lines" 2>/dev/null | head -n 20)
|
|
267
|
+
done
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
gate_summary() {
|
|
271
|
+
# Trả về mã thoát: 1 nếu phải chặn, 0 nếu không.
|
|
272
|
+
local what=${1:-thay đổi}
|
|
273
|
+
if [ "$GATE_N_BLOCK" -gt 0 ]; then
|
|
274
|
+
gate_say "Từ chối $what: $GATE_N_BLOCK vi phạm mức CHẶN, $GATE_N_WARN cảnh báo, $GATE_N_ALLOWED ngoại lệ đã khai."
|
|
275
|
+
gate_say "$TEXT_BLOCK"
|
|
276
|
+
return 1
|
|
277
|
+
fi
|
|
278
|
+
if [ "$GATE_N_WARN" -gt 0 ]; then
|
|
279
|
+
if [ "$GATE_MODE" = audit ]; then gate_say "Chế độ audit: $GATE_N_WARN phát hiện, đã ghi log, cho qua. $TEXT_AUDIT"
|
|
280
|
+
else gate_say "Cho qua với $GATE_N_WARN cảnh báo, $GATE_N_ALLOWED ngoại lệ đã khai. $TEXT_WARN"; fi
|
|
281
|
+
return 0
|
|
282
|
+
fi
|
|
283
|
+
[ "$GATE_QUIET" = 1 ] || gate_say "Đạt: không phát hiện vi phạm ($GATE_N_ALLOWED ngoại lệ đã khai)."
|
|
284
|
+
return 0
|
|
285
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Điểm vào của Cổng kiểm soát ATBMTT (VNPTNAN_GATE).
|
|
3
|
+
#
|
|
4
|
+
# gate-scan.sh staged # quét thay đổi đã stage (pre-commit)
|
|
5
|
+
# gate-scan.sh range <old> <new> # quét các commit trong khoảng (pre-push); <old> có thể là 0000... (nhánh mới)
|
|
6
|
+
# gate-scan.sh receive # đọc "old new ref" từ stdin (pre-push và pre-receive của GitLab)
|
|
7
|
+
# gate-scan.sh commit <sha> # quét một commit
|
|
8
|
+
# gate-scan.sh tree [thư-mục] # quét toàn bộ cây làm việc (đánh giá repo cũ, CI)
|
|
9
|
+
# gate-scan.sh deps [thư-mục] # kiểm dependency và giấy phép
|
|
10
|
+
#
|
|
11
|
+
# Mã thoát: 0 cho qua, 1 từ chối, 2 lỗi cấu hình.
|
|
12
|
+
set -u
|
|
13
|
+
ENGINE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
14
|
+
# shellcheck source=gate-lib.sh
|
|
15
|
+
. "$ENGINE_DIR/gate-lib.sh"
|
|
16
|
+
|
|
17
|
+
mode=${1:-}
|
|
18
|
+
shift || true
|
|
19
|
+
|
|
20
|
+
ZERO='^0+$'
|
|
21
|
+
TMP=$(mktemp -d 2>/dev/null || mktemp -d -t gate)
|
|
22
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
23
|
+
|
|
24
|
+
repo_root() { git rev-parse --show-toplevel 2>/dev/null || pwd; }
|
|
25
|
+
|
|
26
|
+
init() {
|
|
27
|
+
local root
|
|
28
|
+
root=$(repo_root)
|
|
29
|
+
GATE_REPO_NAME=${GL_PROJECT_PATH:-$(basename "$root")}
|
|
30
|
+
gate_load_conf "$root"
|
|
31
|
+
gate_load_rules || exit 2
|
|
32
|
+
gate_load_allow "$root/.vnptnan-gate/allow.rules"
|
|
33
|
+
# Trên server, file ngoại lệ nằm trong commit mới, đọc từ nhánh được đẩy nếu có.
|
|
34
|
+
if [ "$GATE_SERVER" = 1 ] && [ -n "${GATE_ALLOW_REV:-}" ]; then
|
|
35
|
+
if git cat-file -e "$GATE_ALLOW_REV:.vnptnan-gate/allow.rules" 2>/dev/null; then
|
|
36
|
+
git show "$GATE_ALLOW_REV:.vnptnan-gate/allow.rules" > "$TMP/allow.rules" && gate_load_allow "$TMP/allow.rules"
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Các dòng được thêm của một diff, bỏ dấu +, bỏ \r. $1 ... là đối số cho git diff.
|
|
42
|
+
added_lines() {
|
|
43
|
+
git diff --no-color --no-ext-diff -U0 --diff-filter=ACMR "$@" 2>/dev/null \
|
|
44
|
+
| grep -E '^\+' | grep -vE '^\+\+\+ ' | sed 's/^+//; s/\r$//'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
scan_staged() {
|
|
48
|
+
GATE_STAGE=pre-commit
|
|
49
|
+
local path blob
|
|
50
|
+
while IFS= read -r -d '' path; do
|
|
51
|
+
[ -n "$path" ] || continue
|
|
52
|
+
added_lines --cached -- "$path" > "$TMP/lines"
|
|
53
|
+
git show ":$path" > "$TMP/full" 2>/dev/null || : > "$TMP/full"
|
|
54
|
+
gate_scan_file "$path" "$TMP/lines" "$TMP/full"
|
|
55
|
+
done < <(git diff --cached --name-only --diff-filter=ACMR -z)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
scan_commit() {
|
|
59
|
+
# $1 sha. Commit merge chỉ quét những dòng merge tự thêm (so với cha đầu tiên).
|
|
60
|
+
local sha=$1 path short
|
|
61
|
+
short=$(git rev-parse --short "$sha" 2>/dev/null || echo "$sha")
|
|
62
|
+
while IFS= read -r -d '' path; do
|
|
63
|
+
[ -n "$path" ] || continue
|
|
64
|
+
printf '%s' "$path" | grep -qiE '(^|/)(package\.json|[^/]+\.csproj|Directory\.Packages\.props|packages\.config)$' && DEPS_TOUCHED=1
|
|
65
|
+
git diff --no-color --no-ext-diff -U0 --diff-filter=ACMR "$sha^" "$sha" -- "$path" 2>/dev/null \
|
|
66
|
+
| grep -E '^\+' | grep -vE '^\+\+\+ ' | sed 's/^+//; s/\r$//' > "$TMP/lines"
|
|
67
|
+
if ! git rev-parse -q --verify "$sha^" >/dev/null 2>&1; then
|
|
68
|
+
# Commit gốc: mọi dòng đều là dòng thêm.
|
|
69
|
+
git show "$sha:$path" 2>/dev/null | sed 's/\r$//' > "$TMP/lines"
|
|
70
|
+
fi
|
|
71
|
+
git show "$sha:$path" > "$TMP/full" 2>/dev/null || : > "$TMP/full"
|
|
72
|
+
GATE_PATH_PREFIX="$short:" gate_scan_file "$path" "$TMP/lines" "$TMP/full"
|
|
73
|
+
done < <(git diff-tree -r -z --no-commit-id --name-only --diff-filter=ACMR --root "$sha" 2>/dev/null)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
scan_range() {
|
|
77
|
+
# $1 old, $2 new, $3 ref (tuỳ chọn)
|
|
78
|
+
local old=$1 new=$2 ref=${3:-} list n=0
|
|
79
|
+
if printf '%s' "$new" | grep -qE "$ZERO"; then return 0; fi # xoá nhánh: không có gì để quét
|
|
80
|
+
if printf '%s' "$old" | grep -qE "$ZERO"; then
|
|
81
|
+
# Nhánh mới: chỉ các commit chưa có ở bất kỳ ref nào khác.
|
|
82
|
+
if [ "$GATE_SERVER" = 1 ]; then list=$(git rev-list "$new" --not --all 2>/dev/null)
|
|
83
|
+
else list=$(git rev-list "$new" --not --remotes 2>/dev/null); fi
|
|
84
|
+
else
|
|
85
|
+
list=$(git rev-list "$old..$new" 2>/dev/null)
|
|
86
|
+
fi
|
|
87
|
+
[ -n "$list" ] || return 0
|
|
88
|
+
n=$(printf '%s\n' "$list" | wc -l | tr -d ' ')
|
|
89
|
+
if [ "$n" -gt "$GATE_MAX_COMMITS" ]; then
|
|
90
|
+
gate_say "Khoảng đẩy có $n commit, chỉ quét $GATE_MAX_COMMITS commit mới nhất (MAX_COMMITS). Phần còn lại cần quét bằng: vnptnan-gate scan tree"
|
|
91
|
+
list=$(printf '%s\n' "$list" | head -n "$GATE_MAX_COMMITS")
|
|
92
|
+
fi
|
|
93
|
+
[ -n "$ref" ] && [ "$GATE_QUIET" != 1 ] && gate_say "Quét $n commit lên $ref"
|
|
94
|
+
GATE_ALLOW_REV=$new
|
|
95
|
+
while IFS= read -r sha; do [ -n "$sha" ] && scan_commit "$sha"; done <<< "$list"
|
|
96
|
+
# Dependency: chỉ trên máy dev (server không có node_modules/NuGet cache) và chỉ khi khoảng đẩy có đổi file khai báo package.
|
|
97
|
+
if [ "$GATE_DEPS" != off ] && [ "$GATE_SERVER" != 1 ] && [ "${DEPS_TOUCHED:-0}" = 1 ]; then
|
|
98
|
+
deps_check "$(repo_root)"
|
|
99
|
+
fi
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
scan_receive() {
|
|
103
|
+
local old new ref
|
|
104
|
+
GATE_STAGE=${GATE_STAGE_NAME:-pre-push}
|
|
105
|
+
[ "$GATE_SERVER" = 1 ] && GATE_STAGE=pre-receive
|
|
106
|
+
local a b c d
|
|
107
|
+
while read -r a b c d; do
|
|
108
|
+
[ -n "$c" ] || continue
|
|
109
|
+
if [ -n "$d" ]; then
|
|
110
|
+
# pre-push của git: local_ref local_sha remote_ref remote_sha
|
|
111
|
+
scan_range "$d" "$b" "$c"
|
|
112
|
+
else
|
|
113
|
+
# pre-receive của GitLab: old new ref
|
|
114
|
+
scan_range "$a" "$b" "$c"
|
|
115
|
+
fi
|
|
116
|
+
done
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
scan_tree() {
|
|
120
|
+
local dir=${1:-.} f rel size
|
|
121
|
+
GATE_STAGE=tree
|
|
122
|
+
dir=$(cd "$dir" && pwd)
|
|
123
|
+
if git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
124
|
+
# File git theo dõi + file chưa theo dõi nhưng không bị ignore.
|
|
125
|
+
git -C "$dir" ls-files -z --cached --others --exclude-standard
|
|
126
|
+
else
|
|
127
|
+
find "$dir" -type f -print0
|
|
128
|
+
fi > "$TMP/files"
|
|
129
|
+
while IFS= read -r -d '' f; do
|
|
130
|
+
[ -n "$f" ] || continue
|
|
131
|
+
rel=${f#"$dir"/}
|
|
132
|
+
[ -f "$dir/$rel" ] || continue
|
|
133
|
+
size=$(wc -c < "$dir/$rel" | tr -d ' ')
|
|
134
|
+
sed 's/\r$//' "$dir/$rel" > "$TMP/lines" 2>/dev/null || continue
|
|
135
|
+
gate_scan_file "$rel" "$TMP/lines" "$TMP/lines" "$size"
|
|
136
|
+
done < "$TMP/files"
|
|
137
|
+
[ "$GATE_DEPS" != off ] && deps_check "$dir"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
deps_check() {
|
|
141
|
+
# shellcheck source=gate-deps.sh
|
|
142
|
+
. "$ENGINE_DIR/gate-deps.sh"
|
|
143
|
+
gate_deps_scan "$1"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
case "$mode" in
|
|
147
|
+
staged) init; scan_staged; gate_summary "commit" ;;
|
|
148
|
+
range) init; GATE_STAGE=pre-push; scan_range "${1:?old}" "${2:?new}" "${3:-}"; gate_summary "push" ;;
|
|
149
|
+
receive) init; scan_receive; gate_summary "push" ;;
|
|
150
|
+
commit) init; GATE_STAGE=commit; scan_commit "${1:?sha}"; gate_summary "commit" ;;
|
|
151
|
+
tree) init; scan_tree "${1:-.}"; gate_summary "cây làm việc" ;;
|
|
152
|
+
deps) init; GATE_STAGE=deps; deps_check "${1:-$(repo_root)}"; gate_summary "dependency" ;;
|
|
153
|
+
version) echo "VNPTNAN_GATE $GATE_VERSION" ;;
|
|
154
|
+
*) echo "Cách dùng: gate-scan.sh staged | range <old> <new> | receive | commit <sha> | tree [dir] | deps [dir]" >&2; exit 2 ;;
|
|
155
|
+
esac
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Kiểm tra prompt TRƯỚC KHI gửi tới mô hình — hook UserPromptSubmit của Claude Code và Codex.
|
|
3
|
+
# Dùng chung rule với cổng commit (rules/gate.rules): rule kind=line và kind=count áp lên nội dung prompt.
|
|
4
|
+
#
|
|
5
|
+
# stdin : JSON của sự kiện UserPromptSubmit (có trường "prompt")
|
|
6
|
+
# stdout: {"decision":"block","reason":"..."} khi phải chặn (Claude Code và Codex cùng hiểu); rỗng khi cho qua
|
|
7
|
+
# Mã thoát luôn 0: hook lỗi thì cho qua (cùng cách Codex xử lý), lần lỗi được ghi log.
|
|
8
|
+
#
|
|
9
|
+
# bash prompt-check.sh --agent claude|codex
|
|
10
|
+
#
|
|
11
|
+
# Khác với cổng commit, ở đây KHÔNG áp dụng ngoại lệ: không đọc .vnptnan-gate/allow.rules và không nhận
|
|
12
|
+
# "gate-allow:" trong prompt, vì prompt không qua merge request nên không có ai duyệt ngoại lệ.
|
|
13
|
+
# Antigravity không có sự kiện trước khi gửi prompt có quyền chặn, nên không dùng được script này.
|
|
14
|
+
|
|
15
|
+
# Không tin biến môi trường: chỉ đọc gate.conf ở thư mục cài (chỉ admin ghi được)
|
|
16
|
+
unset VNPTNAN_GATE_HOME GATE_ROOT GATE_MODE GATE_DEPS GATE_DEPS_AUDIT GATE_SKIP_PATHS GATE_SKIP_EXTRA \
|
|
17
|
+
GATE_MAX_COMMITS GATE_MAX_FILE GATE_QUIET GATE_SERVER
|
|
18
|
+
set -u
|
|
19
|
+
ENGINE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
20
|
+
AGENT=claude
|
|
21
|
+
[ "${1:-}" = "--agent" ] && AGENT=${2:-claude}
|
|
22
|
+
|
|
23
|
+
TMP=$(mktemp -d 2>/dev/null || mktemp -d -t gateprompt)
|
|
24
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
25
|
+
cat > "$TMP/in.json"
|
|
26
|
+
|
|
27
|
+
# Lấy trường "prompt" và giải mã chuỗi JSON (\n, \", \\, \uXXXX kể cả cặp surrogate) bằng perl có sẵn trong Git for Windows,
|
|
28
|
+
# không cần module ngoài. Không có trường prompt thì cho qua.
|
|
29
|
+
perl -e '
|
|
30
|
+
local $/; my $j = <STDIN>;
|
|
31
|
+
exit 3 unless $j =~ /"prompt"\s*:\s*"((?:[^"\\]|\\.)*)"/s;
|
|
32
|
+
my $s = $1; my $out = "";
|
|
33
|
+
while ($s =~ /\G(?:([^\\]+)|\\u([0-9a-fA-F]{4})(?:\\u([0-9a-fA-F]{4}))?|\\(.))/gcs) {
|
|
34
|
+
if (defined $1) { $out .= $1; next }
|
|
35
|
+
if (defined $2) {
|
|
36
|
+
my $c = hex $2;
|
|
37
|
+
if ($c >= 0xD800 && $c <= 0xDBFF && defined $3) { $c = 0x10000 + (($c - 0xD800) << 10) + (hex($3) - 0xDC00) }
|
|
38
|
+
elsif (defined $3) { $out .= chr_utf8($c); $c = hex $3 }
|
|
39
|
+
$out .= chr_utf8($c); next
|
|
40
|
+
}
|
|
41
|
+
my %m = (n => "\n", t => "\t", r => "", b => "", f => "", "/" => "/", "\\" => "\\", "\"" => "\"");
|
|
42
|
+
$out .= exists $m{$4} ? $m{$4} : $4;
|
|
43
|
+
}
|
|
44
|
+
sub chr_utf8 { my $c = shift; my $x = chr($c); utf8::encode($x); return $x }
|
|
45
|
+
print $out;
|
|
46
|
+
' < "$TMP/in.json" > "$TMP/prompt.txt" 2>/dev/null
|
|
47
|
+
rc=$?
|
|
48
|
+
[ "$rc" -eq 3 ] && exit 0
|
|
49
|
+
if [ "$rc" -ne 0 ]; then
|
|
50
|
+
echo "[VNPTNAN_GATE] prompt-check: không đọc được JSON đầu vào, cho qua" >&2
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
[ -s "$TMP/prompt.txt" ] || exit 0
|
|
54
|
+
|
|
55
|
+
# shellcheck source=gate-lib.sh
|
|
56
|
+
. "$ENGINE_DIR/gate-lib.sh"
|
|
57
|
+
gate_load_conf ""
|
|
58
|
+
gate_load_rules >/dev/null 2>&1 || { echo "[VNPTNAN_GATE] prompt-check: không nạp được rule, cho qua" >&2; exit 0; }
|
|
59
|
+
GATE_STAGE="prompt-$AGENT"
|
|
60
|
+
GATE_REPO_NAME=$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
|
|
61
|
+
GATE_QUIET=1
|
|
62
|
+
GATE_NO_INLINE_ALLOW=1 # gate-lib: bỏ qua "gate-allow:" trên dòng
|
|
63
|
+
GATE_SKIP_PATHS='^$' # không bỏ qua gì: đường dẫn giả "<prompt>" phải được quét
|
|
64
|
+
|
|
65
|
+
# Quét; thu các dòng thông báo để làm lý do chặn
|
|
66
|
+
gate_scan_file "<prompt>" "$TMP/prompt.txt" "$TMP/prompt.txt" "$(wc -c < "$TMP/prompt.txt" | tr -d ' ')" 2> "$TMP/findings.txt"
|
|
67
|
+
|
|
68
|
+
if [ "$GATE_N_BLOCK" -gt 0 ]; then
|
|
69
|
+
{
|
|
70
|
+
echo "Cổng kiểm soát ATBMTT đã chặn prompt này trước khi gửi tới AI (Bộ quy tắc sử dụng AI, Điều 5: dữ liệu mức C3 không được đưa vào AI)."
|
|
71
|
+
sed -n 's/^\[VNPTNAN_GATE\] CHAN \([a-z0-9-]*\) <prompt>\(:[0-9]*\)\{0,1\} /- \1: /p' "$TMP/findings.txt" | head -n 8
|
|
72
|
+
echo "Gỡ mật khẩu, khoá, dữ liệu khách hàng khỏi prompt (thay bằng biến môi trường, dữ liệu giả hoặc mô tả bằng lời) rồi gửi lại."
|
|
73
|
+
} > "$TMP/reason.txt"
|
|
74
|
+
perl -e 'local $/; my $r = <STDIN>; $r =~ s/\\/\\\\/g; $r =~ s/"/\\"/g; $r =~ s/\r//g; $r =~ s/\n/\\n/g; $r =~ s/\t/\\t/g;
|
|
75
|
+
print "{\"decision\":\"block\",\"reason\":\"$r\"}\n"' < "$TMP/reason.txt"
|
|
76
|
+
fi
|
|
77
|
+
exit 0
|
package/gate.conf
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Cấu hình cấp bộ công cụ. Dự án ghi đè bằng .vnptnan-gate/gate.conf; biến môi trường GATE_* ghi đè tất cả.
|
|
2
|
+
# MODE: enforce (từ chối commit/push vi phạm mức block) | audit (chỉ ghi log; dùng trong 2 tuần thí điểm)
|
|
3
|
+
MODE=enforce
|
|
4
|
+
# DEPS: block | warn | off — mức tối đa cho phát hiện về dependency/giấy phép
|
|
5
|
+
DEPS=warn
|
|
6
|
+
# DEPS_AUDIT: 1 = chạy npm audit / dotnet list package --vulnerable (cần mạng, chậm); mặc định tắt trong hook, bật trong CI
|
|
7
|
+
DEPS_AUDIT=0
|
|
8
|
+
# Số commit tối đa quét trong một lần push
|
|
9
|
+
MAX_COMMITS=500
|
|
10
|
+
# File lớn hơn (bytes) không quét theo dòng
|
|
11
|
+
MAX_FILE=5242880
|
|
12
|
+
# SKIP_PATHS_EXTRA: regex đường dẫn thêm vào danh sách bỏ qua mặc định (thường đặt trong .vnptnan-gate/gate.conf của dự án)
|
|
13
|
+
#SKIP_PATHS_EXTRA=(^|/)legacy-dump/
|
package/hooks/commit-msg
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Hook commit-msg của VNPTNAN_GATE: ghi vết commit có AI tham gia để thống kê và kiểm toán.
|
|
3
|
+
# Nếu thông điệp có trailer Co-Authored-By của một coding agent mà chưa có trailer AI-Assisted, tự thêm "AI-Assisted: <tool>".
|
|
4
|
+
# Không bao giờ từ chối commit.
|
|
5
|
+
msg=$1
|
|
6
|
+
[ -f "$msg" ] || exit 0
|
|
7
|
+
if grep -qiE '^AI-Assisted:' "$msg"; then exit 0; fi
|
|
8
|
+
tool=$(grep -iE '^Co-Authored-By:.*(claude|codex|openai|antigravity|gemini|copilot)' "$msg" | head -n1 | sed -E 's/^Co-Authored-By:[[:space:]]*//I; s/[[:space:]]*<.*//')
|
|
9
|
+
if [ -n "$tool" ]; then printf '\nAI-Assisted: %s\n' "$tool" >> "$msg"; fi
|
|
10
|
+
exit 0
|
package/hooks/pre-commit
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Hook pre-commit của VNPTNAN_GATE: quét thay đổi đã stage trước khi commit.
|
|
3
|
+
# Cài qua git config hook.vnptnan-gate-commit.* (cấp máy) hoặc copy vào .git/hooks/pre-commit của repo.
|
|
4
|
+
# Chống hạ cổng bằng biến môi trường (vd. GATE_MODE=audit git commit): hook chỉ tin gate.conf.
|
|
5
|
+
unset VNPTNAN_GATE_HOME GATE_ROOT GATE_MODE GATE_DEPS GATE_DEPS_AUDIT GATE_SKIP_PATHS GATE_SKIP_EXTRA GATE_MAX_COMMITS GATE_MAX_FILE GATE_QUIET GATE_SERVER
|
|
6
|
+
root=$(cd "$(dirname "$0")/.." && pwd)
|
|
7
|
+
GATE_STAGE_NAME=pre-commit exec bash "$root/engine/gate-scan.sh" staged
|
package/hooks/pre-push
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Hook pre-push của VNPTNAN_GATE: quét mọi commit sắp rời máy, kể cả commit tri thức của VNPTNAN_HARNESS (dùng lệnh plumbing, không qua pre-commit).
|
|
3
|
+
# stdin: <local ref> <local sha> <remote ref> <remote sha>, một dòng mỗi ref.
|
|
4
|
+
# Chống hạ cổng bằng biến môi trường (vd. GATE_MODE=audit git commit): hook chỉ tin gate.conf.
|
|
5
|
+
unset VNPTNAN_GATE_HOME GATE_ROOT GATE_MODE GATE_DEPS GATE_DEPS_AUDIT GATE_SKIP_PATHS GATE_SKIP_EXTRA GATE_MAX_COMMITS GATE_MAX_FILE GATE_QUIET GATE_SERVER
|
|
6
|
+
root=$(cd "$(dirname "$0")/.." && pwd)
|
|
7
|
+
GATE_STAGE_NAME=pre-push exec bash "$root/engine/gate-scan.sh" receive
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Cài VNPTNAN_GATE làm server hook toàn cục trên máy chủ GitLab tự host (Linux package / Omnibus, mọi bậc kể cả Free).
|
|
3
|
+
# Chạy bằng root trên máy chủ GitLab:
|
|
4
|
+
# sudo bash installer/install-server.sh [-mode enforce|audit]
|
|
5
|
+
# Việc làm: copy bộ công cụ vào /opt/vnptnan-gate (root sở hữu, git chỉ đọc), tạo thư mục custom_hooks/pre-receive.d,
|
|
6
|
+
# đặt symlink 10-vnptnan-gate, tạo /var/log/vnptnan-gate cho tài khoản git ghi. Không cần khởi động lại GitLab.
|
|
7
|
+
# Tài liệu GitLab: Administration > Git server hooks (thư mục custom_hooks toàn cục: gitaly['configuration'][:hooks][:custom_hooks_dir],
|
|
8
|
+
# mặc định /var/opt/gitlab/gitaly/custom_hooks).
|
|
9
|
+
set -eu
|
|
10
|
+
MODE=enforce
|
|
11
|
+
[ "${1:-}" = "-mode" ] && MODE=${2:-enforce}
|
|
12
|
+
SRC=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
13
|
+
DEST=/opt/vnptnan-gate
|
|
14
|
+
HOOKS_DIR=${GITLAB_CUSTOM_HOOKS_DIR:-/var/opt/gitlab/gitaly/custom_hooks}
|
|
15
|
+
GIT_USER=${GITLAB_GIT_USER:-git}
|
|
16
|
+
|
|
17
|
+
[ "$(id -u)" = 0 ] || { echo "Chạy bằng root."; exit 2; }
|
|
18
|
+
id "$GIT_USER" >/dev/null 2>&1 || { echo "Không có tài khoản $GIT_USER. Đặt GITLAB_GIT_USER."; exit 2; }
|
|
19
|
+
|
|
20
|
+
rm -rf "$DEST"; mkdir -p "$DEST"
|
|
21
|
+
cp -r "$SRC"/. "$DEST"/; rm -rf "$DEST/.git"
|
|
22
|
+
sed -i "s/^MODE=.*/MODE=$MODE/" "$DEST/gate.conf"
|
|
23
|
+
chown -R root:root "$DEST"; chmod -R a+rX "$DEST"; chmod +x "$DEST"/server/pre-receive "$DEST"/engine/*.sh
|
|
24
|
+
|
|
25
|
+
mkdir -p "$HOOKS_DIR/pre-receive.d"
|
|
26
|
+
ln -sfn "$DEST/server/pre-receive" "$HOOKS_DIR/pre-receive.d/10-vnptnan-gate"
|
|
27
|
+
chown -R "$GIT_USER":"$GIT_USER" "$HOOKS_DIR"
|
|
28
|
+
|
|
29
|
+
mkdir -p /var/log/vnptnan-gate; chown "$GIT_USER":"$GIT_USER" /var/log/vnptnan-gate; chmod 750 /var/log/vnptnan-gate
|
|
30
|
+
|
|
31
|
+
echo "Đã cài VNPTNAN_GATE $(cat "$DEST/VERSION") làm server hook toàn cục (MODE=$MODE)."
|
|
32
|
+
echo "Kiểm tra: đẩy một commit chứa 'AKIAIOSFODNN7EXAMPLE' lên nhánh thử; GitLab phải từ chối với dòng GL-HOOK-ERR."
|
|
33
|
+
echo "Log: /var/log/vnptnan-gate/gate.log. Để quét định kỳ: xem ci/.gitlab-ci.yml khi bật CI."
|