Skip to content

Commit 9d8dbdd

Browse files
ci: 推送后隐私复查(privacy-gate)
每次推送后用加密的敏感词表复查新增内容,命中则此检查失败并发邮件通知。 只报位置和规则序号,不回显命中原文。本机推送前已有同一套检查,这里兜住 别的机器推的、被绕过的和网页上直接改的。 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 59ccc17 commit 9d8dbdd

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

‎.github/workflows/privacy-gate.yml‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# 隐私闸 · GitHub 这一侧的第二道(复查,不是拦截)
2+
#
3+
# github.com 上没有办法在"收到推送之前"拒绝 —— 那是 GitHub 企业版自建服务器才有的功能。
4+
# 这个 Action 在推上来**之后**跑:公开仓库在推上来的那一刻已经能被人看到,它收不回来。
5+
# 它的用处是兜住本地那道闸管不到的推送:别的机器推的、--no-verify 绕过的、网页上直接改的。
6+
# 失败时 GitHub 会按你的通知设置发邮件。
7+
#
8+
# 用法:把本文件放到仓库的 .github/workflows/privacy-gate.yml,然后
9+
# gh secret set PRIVACY_TERMS -R <owner>/<repo> < ~/.compound-memory/privacy-terms.txt
10+
# gh variable set PRIVACY_MODE -R <owner>/<repo> --body publish # 可选,默认 strict
11+
# 词表以加密密文存在 GitHub 上,只在这个 Action 运行时可见,不进仓库、不出现在日志里。
12+
name: privacy-gate
13+
on: [push]
14+
permissions:
15+
contents: read
16+
jobs:
17+
scan:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
- name: 扫这次推上来的新增内容
24+
env:
25+
TERMS: ${{ secrets.PRIVACY_TERMS }}
26+
MODE: ${{ vars.PRIVACY_MODE }}
27+
BEFORE: ${{ github.event.before }}
28+
AFTER: ${{ github.sha }}
29+
run: |
30+
python3 - <<'PY'
31+
import os, re, subprocess, sys
32+
terms_txt = os.environ.get('TERMS', '')
33+
if not terms_txt.strip():
34+
print('::error::没有设置 PRIVACY_TERMS 密文,隐私复查没法跑'); sys.exit(1)
35+
mode = os.environ.get('MODE') or 'strict'
36+
tier, terms = 'private', []
37+
for s in (l.strip() for l in terms_txt.splitlines()):
38+
if not s or s.startswith('#'):
39+
continue
40+
if s in ('[private]', '[brand]'):
41+
tier = s[1:-1]; continue
42+
if mode == 'publish' and tier != 'private':
43+
continue
44+
try:
45+
terms.append(re.compile(s))
46+
except re.error:
47+
pass
48+
before, after = os.environ['BEFORE'], os.environ['AFTER']
49+
rng = [after] if set(before) == {'0'} else [f'{before}..{after}']
50+
log = subprocess.run(['git', 'log', '-p', '--no-color', '--format=%x00%h %B', *rng],
51+
capture_output=True, text=True, errors='replace').stdout
52+
n = 0
53+
for line in log.splitlines():
54+
if line.startswith(('+++', '---')) or line.startswith('-'):
55+
continue
56+
for rx in terms:
57+
if rx.search(line):
58+
n += 1
59+
# 只报位置和第几条规则,不回显命中的原文 —— Action 日志对有读权限的人可见
60+
print(f'::error::第 {n} 处命中(规则 #{terms.index(rx) + 1}),在新增内容里')
61+
break
62+
print(f'隐私复查({mode}):词表 {len(terms)} 条,命中 {n} 处')
63+
sys.exit(1 if n else 0)
64+
PY

0 commit comments

Comments
 (0)