自动化检测与追踪GitHub上的虚假互动
一个 JS Labs 项目 —
属于 AI Slop Intelligence 倡议的一部分。
每天运行。给每个可疑账户打分。检测协调性的机器人活动。
直接在受影响的仓库上提交问题,以便维护者采取行动。
支持此项目
BTC 3QjWqhQbHdHgWeYHTpmorP8Pe1wgDjJy54
ETH 0x5851e6145F4773d1585b8686095FB16E368a4dA1
ZEC t1KSR5YkNPbjqRSCoLKo5AddFWdm9Kzxh1B
GitHub 星标是一种信任信号。开发者据此决定评估什么、依赖什么、推荐什么。那个信号正在被系统性地破坏。
在 2024-2026 年的人工智能繁荣期间,出现了一个机器人农场产业,为低质量、通常恶意的仓库制造可信度。一个在 48 小时内获得 800 个星标的项目,对于浏览搜索结果的开发者来说,看起来是合法的。这就是关键所在。虚假互动的目标不是星标本体,而是这些星标所产生的社会认同,以及这种社会认同所影响的下游决策。
这种模式是可识别的。同一周创建的账户,没有个人简介,没有关注者,没有原创仓库,在两小时内为相同的 15 个仓库加星标。不是一个活动,而是每天同时运行数十个,涉及数千个账户。数据显示在某些仓库中,185 个互动者中有 185 个是机器人。百分之百的虚假率。整个热门排名建立在虚无之上。
phantomstars 的构建是因为这个问题是可处理的。GitHub 公共 API 中的信噪比目前仍然足够高,以至于协调性活动留下了清晰的指纹。该项目读取这些指纹,发布原始数据,并直接通知受影响的仓库维护者。
这是 JS Labs 更广泛的 AI Slop Intelligence 工作的一部分,正在进行的研究,旨在探究低质量 AI 生成内容淹没开发者生态系统的机制和可测量影响。虚假互动不是一个外围问题。它是让劣质内容出现在真正用户面前的传播机制。
phantomstars 每天运行一个 GitHub Actions 作业,该作业:
r/osinttools 和 r/coolgithubprojects 板块最近 2 天的帖子中提取 GitHub 仓库链接,作为额外候选仓库无服务器。无数据库。无基础设施账单。
会。 当仓库的虚假率超过 40% 或检测到协调性活动时,phantomstars 会在该仓库上直接打开一个 issue。该 issue 包含完整的可疑账户表、活动成员身份、综合分数以及账户创建日期:维护者进行调查并向 GitHub 报告所需的一切信息。
如果目标仓库禁用了 issue,则通知会静默跳过,并记录在扫描日志中。
可以。
owner/repo 格式提交仓库,并运行定向扫描。为什么要分开:
可以。如果你的账户出现在 data/suspects.jsonl 中,并且你认为分类有误,请使用提供的模板提交一个误报 issue。报告会在添加到白名单之前进行人工审查。白名单存储在 data/allowlist.txt 中;列在其中的账户将从所有未来的扫描和可疑账户账本中排除。
活动 ID(例如 c-a3f9b2e1)是一个 确定性 8 字符十六进制指纹,派生自该活动中成员登录名的排序集合的 SHA-256 哈希。同一组账户在不同的扫描运行中会产生相同的活动 ID,从而实现纵向追踪。它不是仓库名称、用户名或任何外部标识符。
稳定性: 只要活动的成员集合不变,该 ID 就是稳定的。如果在两次扫描之间添加或暂停了机器人,ID 就会改变,因为成员身份发生了变化。这是预期行为,反映了机器人农场组成的真实漂移。
会。 每个账户的创建日期都通过 GitHub GraphQL API(createdAt 字段)获取,并作为 account_created_at 存储在每条可疑记录中。它也是账户年龄评分的主要输入,是虚假账户最强的单一信号。在互动前 2 天内创建的账户仅凭年龄就获得 1.0 分。
单个分数具有显著的误报率。一个资料稀疏的新开发者可能合理获得 0.75+ 分。该工具通过在提交 issue 之前要求活动级证据来解决这个问题;单个可疑账户是不够的。一个协调的集群,包含 40 多个账户,全部在同一周创建,全部得分 0.75+,全部在 90 分钟内互动,则是另一回事。这才是置信度变得可行的时候。
数据始终是概率性的。issue 正文明确说明了这一点。目标是给维护者提供信号和原始证据,让他们自己做出判断。
每个账户会收到一个综合可疑分数(0.0 = 干净,1.0 = 可能虚假),来自四个信号:
分类阈值:
| 分数 | 分类 |
|---|---|
| ≥ 0.75 | likely_fake |
| ≥ 0.45 | suspicious |
| < 0.45 | clean(不存储) |
一个活动是指一组 ≥ 4 个可疑账户,在同一仓库的 3 小时窗口内都进行了互动。该算法使用并查集构建连通分量;在窗口内共同互动的账户会被合并,任何超过最小大小的分量都会被标记为协调性活动。
活动 ID 是排序成员集合的稳定 SHA-256 指纹。在连续几天检测到的同一个活动,只要成员身份不变,就会有相同的 ID。
为什么活动才是真正的信号: 单个分数具有显著的误报率。一个新开发人员资料稀疏,单独可能获得 0.80 分。四十个账户全部得分 0.75+,同一周内创建,全部在 90 分钟内为同一个仓库加星标,这不是巧合。活动信号才是数据变得可行的地方:它区分了可疑数据点和协调性操作的证据。
所有发现都提交到 data/suspects.jsonl 和 data/repos.jsonl,每行一个 JSON 记录,只追加。GitHub Actions 作业摘要(每次运行后在 Actions UI 中可见)提供格式化的每次扫描报告。
suspects.jsonl — 每次扫描每个被标记的账户一条记录:```json { "login": "user98432", "account_age_score": 0.9, "profile_score": 0.8, "repo_pattern_score": 0.8, "activity_score": 0.85, "composite": 0.842, "classification": "likely_fake", "campaign_id": "c-a3f9b2e1", "scan_date": "2026-05-17", "account_created_at": "2026-05-15", "target_repos": ["owner/repo-a", "owner/repo-b"] }
**repos.jsonl** — 每个扫描每个目标仓库一条记录:```json
{
"full_name": "owner/suspicious-repo",
"total_scanned": 87,
"likely_fake": 62,
"suspicious": 18,
"known_likely_fake": 27,
"known_likely_fake_ratio": 0.310,
"repeat_offenders": 11,
"allowlisted_excluded": 3,
"fakeness_ratio": 0.713,
"classification": "likely_fake",
"campaign_count": 3,
"discovery_sources": ["github_search_recent", "reddit_osinttools"],
"event_sample_complete": false,
"scan_date": "2026-05-17"
}
查询示例:```bash
jq 'select(.scan_date == "2026-05-17" and .classification == "likely_fake") | .login' data/suspects.jsonl
jq 'select(.account_created_at >= "2026-05-14") | [.login, .account_created_at, .classification] | @tsv' -r data/suspects.jsonl
jq 'select(.scan_date == "2026-05-17") | [.full_name, .fakeness_ratio, .likely_fake] | @tsv' -r data/repos.jsonl | sort -t$'''\t''' -k2 -rn
jq 'select(.scan_date == "2026-05-17") | [.full_name, .known_likely_fake_ratio, .repeat_offenders] | @tsv' -r data/repos.jsonl | sort -t$'''\t''' -k2 -rn
jq 'select(.campaign_id == "c-a3f9b2e1") | [.login, .account_created_at, .composite] | @tsv' -r data/suspects.jsonl
jq 'select(.login == "user98432") | .target_repos[]' data/suspects.jsonl
jq 'select(.fakeness_ratio >= 0.6) | [.full_name, .fakeness_ratio, .campaign_count] | @tsv' -r data/repos.jsonl | sort -t$'\t' -k2 -rn
## 设置
### 1. Fork 本仓库
你的 Fork 拥有数据所有权。每次每日运行后,结果会提交到你的 Fork 上的 `data/suspects.jsonl` 和 `data/repos.jsonl`。
### 2. 添加 GitHub PAT 密钥
创建一个**经典**个人访问令牌,需包含以下作用域:
- `public_repo`:读取公共仓库事件和星标者,在公共仓库上创建 Issue
- `read:user`:通过 GraphQL 获取用户资料
**设置 → 密钥和变量 → 操作 → 新建仓库密钥** → 命名为 `GH_TOKEN`。
> 默认的 `GITHUB_TOKEN` 具有受限的速率限制,无法以全容量调用用户 GraphQL 端点。需要使用 PAT。
### 3. 启用 Actions
**操作 → 启用 GitHub Actions** 在你的 Fork 上。工作流使用 `Europe/London` 时钟,**英国时间每日 07:00** 运行:
- **英国夏令时期间为 06:00 UTC**
- **格林尼治标准时间期间为 07:00 UTC**
无需额外调度环境变量。GitHub Actions 的 Cron 仅支持 UTC,因此工作流在两个 UTC 小时都会触发,但仅在本地伦敦时间为 07:00 时才会继续执行。可通过 **操作 → Daily Phantom Stars Scan → 运行工作流** 进行手动触发。
每次运行后,格式化的扫描报告会在 **操作 → [运行] → 摘要** 中显示。
### 4. 本地运行```bash
git clone https://github.com/YOUR_USERNAME/phantomstars.git
cd phantomstars
python -m venv venv && source venv/bin/activate
pip install -e .
GH_TOKEN=ghp_your_token python -m phantomstars.main
对于安装后的临时本地运行:```bash GH_TOKEN=ghp_your_token python -m phantomstars.main
扫描一个仓库而非正常的发现集:```bash
PHANTOMSTARS_TARGET_REPO=owner/repo GH_TOKEN=ghp_your_token python -m phantomstars.main
用户可以通过两种方式请求一次性仓库检查:
Repo Check Request 问题模板,提供目标仓库及请求的深度。target_repo: owner/reporequest_depth: recent 或 lifetime-request当前行为:
recent:立即运行针对性的近期互动扫描。lifetime-request:仅针对该仓库运行跨越历史星标和复刻的生存期扫描。生存期模式的防护措施:
phantomstars/ ├── .github/ │ ├── workflows/daily-scan.yml # Runs daily at 07:00 Europe/London │ └── ISSUE_TEMPLATE/false_positive.yml ├── src/phantomstars/ │ ├── config.py # All constants, no argparse, no env parsing │ ├── models.py # Frozen dataclasses │ ├── github_client.py # REST + GraphQL, tenacity retries, rate-limit aware │ ├── heuristics.py # Per-user composite scoring engine │ ├── campaigns.py # Timestamp clustering + union-find │ ├── storage.py # JSONL append + query helpers │ ├── reporter.py # README dashboard injector │ ├── notifier.py # GitHub Issues notifier (files on targeted repos) │ └── main.py # Orchestration entry point ├── tests/ │ ├── conftest.py │ ├── test_heuristics.py │ └── test_campaigns.py ├── data/ │ ├── suspects.jsonl # Append-only account findings ledger │ ├── repos.jsonl # Append-only per-repo intelligence │ └── allowlist.txt # Accounts excluded from future scans └── pyproject.toml
---
## 限制与已知故障模式
- **Events API上限:** 每个仓库最多300个近期事件。每天获得数千星标的仓库将只有部分覆盖。
- **覆盖标志:** 达到300事件上限的仓库在报告和仪表板中被标记为`capped`;这些仓库的比率是保守样本,而非全天计数。
- **搜索索引延迟:** GitHub的搜索索引是最终一致性的。在扫描边界前几秒创建的仓库可能会被遗漏。
- **启发式漂移:** 机器人运营商会适应。评分权重可能需要定期调整;在`config.py`中调整常量。
- **个体误报:** 一个新开发者如果资料稀疏,孤立评分可能达到0.75以上。活动成员身份是高置信度信号。
- **活动ID漂移:** 如果在两次扫描之间僵尸农场的成员发生变化(机器人被暂停、新机器人加入),活动ID会改变。这反映了实际的活动演变,而非缺陷。
- **速率限制:** 经过身份验证的PAT每小时5000次API请求。对于标准趋势页面大小来说完全在限制之内。
- **问题被禁用:** 一些目标仓库禁用了问题。这些仓库的通知会被静默跳过。
---
## 误报处理流程
如果你的账号出现在`data/suspects.jsonl`中并且你认为被错误分类:
1. 找到你的条目: `jq 'select(.login == "YOUR_LOGIN")' data/suspects.jsonl`
2. [提交误报问题](https://raw.githubusercontent.com/tg12/issues/new?template=false_positive.yml),附上你的登录名、分类、扫描日期和解释
3. 报告会被人工审核。验证为误报的条目会被添加到`data/allowlist.txt`中,并从所有未来的扫描、仓库比率和问题通知中排除。
注意:提交问题不会修改或删除任何现有数据。嫌疑人账本只追加不删除。允许列表仅影响未来的扫描。
---
## 贡献```bash
pip install -e ".[dev]"
python -m black .
python -m ruff check .
python -m mypy src
python -m pytest
所有四项检查必须全部通过才能提交PR。
本工具使用官方 GitHub API 对公共 GitHub 数据进行只读分析。如果在目标仓库中提交了问题,它们包含基于概率的发现,并明确标记为自动生成。发现结果是指标,而非指控。误报存在且属于预期情况。
本工具由 AI 作为编码伙伴构建,以应对部分由 AI 引起的生态系统问题。
Apache 2.0。详见 LICENSE
由 tg12 构建 · GitHub
一个 JS Labs 项目 · AI 垃圾情报仪表板
| 日期 | 已扫描 | 可能虚假 | 可疑 | 活动 | 新增虚假 (24h) |
|---|
| 2026-06-16 | 1846 | 221 | 1625 | 56 | 190 |
| 2026-06-15 | 2274 | 418 | 1856 | 61 | 397 |
| 2026-06-14 | 1953 | 355 | 1598 | 44 | 310 |
| 2026-06-13 | 2012 | 301 | 1711 | 47 | 251 |
| 2026-06-12 | 2298 | 336 | 1962 | 57 | 300 |
| 2026-06-11 | 1957 | 385 | 1572 | 42 | 356 |
| 2026-06-10 | 2043 | 687 | 1356 | 50 | 665 |
| 2026-06-09 | 2199 | 690 | 1509 | 44 | 632 |
| 2026-06-08 | 1913 | 450 | 1463 | 50 | 424 |
| 2026-06-07 | 1797 | 658 | 1139 | 30 | 618 |
| 2026-06-06 | 2625 | 712 | 1913 | 40 | 620 |
| 2026-06-05 | 2403 | 673 | 1730 | 53 | 617 |
| 2026-06-04 | 2237 | 441 | 1796 | 41 | 367 |
| 2026-06-03 | 2331 | 488 | 1843 | 53 | 431 |
| 2026-06-02 | 2795 | 773 | 2022 | 37 | 616 |
| 2026-06-01 | 2490 | 533 | 1957 | 39 | 355 |
| 2026-05-31 | 2302 | 458 | 1844 | 32 | 280 |
| 2026-05-30 | 2576 | 530 | 2046 | 20 | 356 |
| 2026-05-29 | 2838 | 733 | 2105 | 42 | 369 |
| 2026-05-28 | 2748 | 694 | 2054 | 39 | 396 |
| 2026-05-27 | 2193 | 560 | 1633 | 32 | 491 |
| 2026-05-26 | 1930 | 236 | 1694 | 43 | 190 |
| 2026-05-25 | 1526 | 214 | 1312 | 32 | 158 |
| 2026-05-24 | 2170 | 358 | 1812 | 39 | 265 |
| 2026-05-23 | 2548 | 426 | 2122 | 43 | 317 |
| 2026-05-22 | 2318 | 340 | 1978 | 47 | 247 |
| 2026-05-21 | 1981 | 348 | 1633 | 25 | 277 |
| 2026-05-20 | 1613 | 268 | 1345 | 23 | 163 |
| 2026-05-19 | 5463 | 630 | 4121 | 67 | 442 |
| 2026-05-18 | 8838 | 670 | 7950 | 128 | 340 |
| 仓库 | 互动者 | 可能虚假 | 已知虚假 % | 虚假率 % | 活动 | 覆盖范围 | 来源 |
|---|
| freeCodeCamp/freeCodeCamp | 264 | 26 | 0.0% | 9.8% | 1 | complete | github_trending |
| Lolner95/use-kimi-on-cursor | 116 | 17 | 26.7% | 14.7% | 1 | complete | github_search_recent |
| zmustafa/AzureSupportAgent | 33 | 16 | 36.4% | 48.5% | 1 | complete | github_search_recent |
| Free-TV/IPTV | 291 | 14 | 0.3% | 4.8% | 1 | complete | github_trending |
| Panniantong/Agent-Reach | 292 | 14 | 0.3% | 4.8% | 1 | capped | github_trending |
| jwasham/coding-interview-university | 288 | 12 | 0.3% | 4.2% | 1 | capped | github_trending |
| Alex-Shayo/bakkes-mod-install | 37 | 10 | 0.0% | 27.0% | 1 | complete | github_search_recent |
| Timgt86/yt-downloader-savetube | 37 | 10 | 0.0% | 27.0% | 1 | complete | github_search_recent |
| devassisthub/Zelda-TP-PC-Port | 35 | 9 | 0.0% | 25.7% | 1 | complete | github_search_recent |
| imohammedyasin/steam-tools | 35 | 9 | 0.0% | 25.7% | 1 | complete | github_search_recent |
| lol-toolkit/ltk-manager-lol | 35 | 9 | 0.0% | 25.7% | 1 | complete | github_search_recent |
| tor-browser-download/tor-browser | 36 | 9 | 0.0% | 25.0% | 1 | complete | github_search_recent |
| claude-code-ai-anthropic/free-claude-code-ai-desktop-app | 38 | 9 | 0.0% | 23.7% | 1 | complete | github_search_recent |
| vitaliikapliuk/modelharness | 60 | 9 | 31.7% | 15.0% | 1 | complete | github_search_recent |
| shiyu-coder/Kronos | 282 | 9 | 1.1% | 3.2% | 1 | complete | github_trending |
| snanas/Forza-Horizon-Spotify-Radio | 34 | 8 | 0.0% | 23.5% | 1 | complete | github_search_recent |
| bingook/bingo | 45 | 8 | 2.2% | 17.8% | 2 | complete | github_search_recent |
| darricke/claude-fable-5-desktop-free | 49 | 8 | 0.0% | 16.3% | 1 | complete | github_search_recent |
| Ponzuu84/MaaNTE | 32 | 7 | 0.0% | 21.9% | 1 | complete | github_search_recent |
| iDesignStudioz/yellowkey-bitlocker | 33 | 7 | 0.0% | 21.2% | 1 | complete | github_search_recent |
| chatwoot/chatwoot | 230 | 7 | 0.0% | 3.0% | 1 | complete | github_trending |
| Open-Builders/pumpfun-bundler-pump.fun-bundler-solana-token-bundler-bot | 18 | 6 | 33.3% | 33.3% | 1 | complete | github_search_recent |
| taisly/agent | 23 | 6 | 17.4% | 26.1% | 2 | complete | github_search_recent |
| iptv-org/iptv | 193 | 6 | 0.0% | 3.1% | 1 | complete | github_trending |
| itsfatduck/optimizerDuck | 293 | 6 | 0.3% | 2.0% | 1 | complete | github_trending |
| 信号 | 权重 | 测量方式 |
|---|
| 账户年龄 | 35% | < 2 days → 1.00 · < 7 days → 0.90 · < 30 days → 0.55 · < 90 days → 0.20 · 更旧 → 0.00 |
| 资料完整度 | 30% | 加分项:无个人简介 (+0.25)、无地点 (+0.15)、无公司 (+0.10)、零关注者 (+0.30)、零关注 (+0.10)、机器人模式用户名 (+0.20) |
| 仓库模式 | 25% | 零仓库 → 0.90 · 所有仓库都是 fork → 0.80 · fork 比例 >85% → 0.55 |
| 活动历史 | 10% | 超过14天、零仓库且零社交图谱的账户 → 0.80(幽灵账户)。仅零仓库 → 0.60。全部 fork 且无社交图谱 → 0.50 |