扫描 Gitee 725 万个公共代码库发现 26,400 个有效凭据
26,400 live credentials found scanning 7.25M repos on China's Gitee
独立安全研究员 Ben Zimmermann 联合 MongoDB 团队扫描了 Gitee 上的 7,25,6531 个公共代码库,利用 TruffleHog 与 Kingfisher 双扫描器配合 Modal 服务端计算平台,共发现了 26,400 个独特的已验证活跃凭据。
General Information
General Information
Documentation
Community Forums
Blog
University
Products Platform
Atlas Build with the intelligent data platform Platform Services
Database Power critical apps with real-time data
Search Deliver fast, relevant search at scale
Vector Search Build AI apps with accurate context
Voyage AI Embed meaning and rerank results
Atlas Agent Engine Build, deploy, and govern agents in production Self Managed
Enterprise Advanced Manage your apps on your infrastructure
Community Edition Develop locally with MongoDB, at no cost Tools
Compass Work with MongoDB data in a GUI
Integrations Integrations with third-party services
Relational Migrator Migrate to MongoDB with confidence View All Products Explore our full product suite MongoDB 9.0 The best version of MongoDB ever built Build with MongoDB Atlas Get started for free in minutes Sign Up Explore Atlas Deployment Options Develop with self-managed MongoDB Learn more Try Community Edition Explore the latest version of MongoDB Download
Resources
Documentation Learn how to build with MongoDB and get started today
Skills and Certifications Develop in-demand skills and earn MongoDB certifications
Books Read books and e-books about MongoDB
MongoDB for Educators Take advantage of educator resources
MongoDB for Students Take advantage of student benefits
Community Join a community of developers
Events and Webinars Find an event or webinar near you Resources Hub Get help building the next big thing with MongoDB
Solutions Use cases Artificial IntelligenceModernizationPaymentsServerless DevelopmentGamingIntelligent SearchEdge and Mobile Industries Financial ServicesTelecommunicationsHealthcareRetailPublic SectorManufacturing Solutions Library Organized and tailored solutions to kick-start projects MongoDB AMP Modernize applications up to 3x faster Learn more Startups and AI Innovators For world-changing ideas and AI pioneers Learn more Customer Case Studies Hear directly from our users See stories
CompanyCareers Start your next adventureBlog Read articles and announcementsNewsroom Read press releases and news stories Partners Learn about our partner ecosystemLeadership Meet our executive teamCompany Learn more about who we are Contact Us Reach out to MongoDB Connect with an expert Investors Visit our investor portal Learn more
Eng
Scanning 7.25 Million Public Repositories on China's Largest Git Platform for Secrets
October 28, 2026・ 7 min read
This guest post by independent security researcher Ben Zimmermann was developed in collaboration with the Kingfisher team at MongoDB. Ben specializes in large-scale credential exposure research across public source-hosting platforms.
TL;DR: I scanned 7,256,531 public repositories on Gitee (China's largest indigenous Git hosting platform), using both TruffleHog and Kingfisher, and found 26,400 unique verified-live credentials. Findings included an active organization-admin credential for a major enterprise AI platform and thousands of live cloud provider access keys.
What is Gitee?
Gitee, launched in 2013 by OSChina, was selected by China's Ministry of Industry and Information Technology as the country's national open-source code hosting platform. Its public API is severely constrained relative to GitHub and GitLab: no paginated projects endpoint, and the on-site code-search feature appears to have been retired years ago. A2021 attempt to study credential exposure on Gitee concluded that its APIs were too limited to scale the research.
Like any large Git platform, it's an attractive surface for credential exposure:
It is Git-based, so secrets buried deep in commit history persist.
It hosts millions of public repositories with active commit volume.
It has no public equivalent of the platform-vendor secret-scanning partnerships that have become standard on GitHub.
Enumerating millions of Gitee repositories
Gitee's public API is severely limited, so enumerating millions of repositories required combining many surfaces: the only official REST endpoint that returned useful data at scale, the platform's Explore feed, per-user repository and stargazer pages on the website itself, and Gitee's internal Elasticsearch widget that backs the on-site search UI.
Bootstrapping a username corpus
The single official REST endpoint that returned useful data at scale was GET /api/v5/search/users?q=. I hit it with three-character prefix queries (aaa, aab, ..., zzz) and combined the results with usernames extracted from the public Explore feed (/explore/all?order=starred|popular|updated). I then expanded that set via each user's following list and the stargazers of popular repositories. This produced a seed corpus of 1,704,759 usernames.
Scraping per-user pages on the frontend
With the username corpus in hand, two per-user pages yielded repositories: gitee.com/{user}/projects (a user's own repos) and gitee.com/{user}/stared_projects (repos the user has starred). Page 1 of /projects returns full HTML; subsequent pages are AJAX-paginated (X-Requested-With: XMLHttpRequest). The stargazer-list pages used during username bootstrap paginate the same way, additionally requiring a CSRF token extracted from the page-1 HTML.
Querying the on-site search widget
A large fraction of the platform sits outside the social graph. To reach it, I used the same internal Elasticsearch endpoint that backs Gitee's on-site search UI: so.gitee.com/v1/search/widget/wong1slagnlmzwvsu5ya. It takes a query ID, a q parameter, and pagination via from + size:
Unformatted
Code Snippet
xxxxxxxxxx
28
1
elastic_scrape_repos.py
2
API_URL = "https://so.gitee.com/v1/search/widget/wong1slagnlmzwvsu5ya"
3
QUERY_ID = "1048"
4
PAGE_SIZE = 20
5
MAX_RESULTS = 200
6
7
8
async def fetch_page(session, username, offset=0, language=None):
9
params = {
10
"query": QUERY_ID,
11
"q": username,
12
"from": str(offset),
13
"size": str(PAGE_SIZE),
14
"sort_by_f": "",
15
}
16
17
18
if language:
19
params["编程语言"] = language # "programming language"
20
21
22
async with session.get(
23
API_URL,
24
params=params,
25
headers={**HEADERS, "userid": str(uuid.uuid4())},
26
) as resp:
27
resp.raise_for_status()
28
return await resp.json()
Each query returned at most 20 results per page, with a hard cap of 200 results total (from + size <= MAX_RESULTS) regardless of how many matches actually existed. For specific usernames, this was usually fine; for common usernames and trigram prefix queries against the widget, the cap silently truncated results.
Escaping the 200-result cap with bucket splitting
The widget's response includes Elasticsearch aggregations that bucket results by language (编程语言) and license (许可证). For the per-username scrape, when a query was capped, I split it by the language buckets the response already returned:
Unformatted
Code Snippet
xxxxxxxxxx
47
1
elastic_scrape_repos.py
2
3
4
MAX_RESULTS = 200
5
PAGE_SIZE = 20
6
7
8
def get_total(data):
9
total = data.get("hits", {}).get("total", {})
10
return total.get("value", 0), total.get("relation", "eq")
11
12
13
def get_language_buckets(data):
14
aggs = data.get("aggregations", {})
15
return [b["key"] for b in aggs.get("编程语言", {}).get("buckets", [])]
16
17
18
After fetching page 1 for a username:
19
total, relation = get_total(data)
20
21
22
if total <= PAGE_SIZE:
23
return all_records
24
25
26
Within the cap: paginate normally
27
if not (total >= MAX_RESULTS and relation == "gte"):
28
for offset in range(PAGE_SIZE, min(total, MAX_RESULTS), PAGE_SIZE):
29
data = await fetch_page(session, username, offset=offset)
30
collect(parse_hits(data, username))
31
return all_records
32
33
34
Capped: split by language buckets that Elastic already returned
35
for lang in get_language_buckets(data):
36
offset = 0
37
while offset < MAX_RESULTS:
38
data = await fetch_page(session, username, offset=offset, language=lang)
39
hits = parse_hits(data, username)
40
collect(hits)
41
42
43
if len(hits) < PAGE_SIZE:
44
break
45
46
47
offset += PAGE_SIZE
For trigram prefix queries against the widget (used to surface repos not associated with any user I'd discovered), I extended this into a BFS over a query tree: capped nodes split first by language, then by license, then by deeper prefix until each branch fit within 200 results.
After deduplicating repo IDs across all sources, this produced a working set of 7,256,531 unique public Gitee repositories.
Wiring up the scan pipeline
The next challenge was cloning and scanning the 7.25 million repositories. To do this in a single Python script, I usedModal, a serverless compute platform popular for AI workloads but well-suited to most parallel jobs. I ran TruffleHog and Kingfisher concurrently against every repository for increased coverage. Each surface verified credentials that the other missed, so running both caught more exposed credentials than either alone.
Figure 1. Dual-scanner pipeline architecture.
The Docker image
The container image is a multi-stage build that pulls both scanner binaries from their upstream releases into a single Python base, plus git and OpenSSH for cloning:
Unformatted
Code Snippet
xxxxxxxxxx
7
1
FROM trufflesecurity/trufflehog:latest AS trufflehog
2
FROM ghcr.io/mongodb/kingfisher:latest AS kingfisher
3
FROM python:3.11-slim
4
COPY --from=trufflehog /usr/bin/trufflehog /usr/local/bin/trufflehog
5
COPY --from=kingfisher /usr/local/bin/kingfisher /usr/local/bin/kingfisher
6
RUN apt-get update && apt-get install -y git openssh-client \
7
&& rm -rf /var/lib/apt/lists/*
The per-repo scan function
A single Modal function bare-clones each repo over SSH and runs both scanners concurrently as subprocesses against the same on-disk clone:
Unformatted
Code Snippet
xxxxxxxxxx
41
1
@app.function(
2
image=image,
3
volumes={"/findings": volume},
4
timeout=300,
5
cpu=(0.125, 2.0),
6
memory=(256, 4096),
7
max_containers=350,
8
)
9
10
11
def scan(repo_url: str):
12
repo_name = repo_url.rstrip("/").split("/")[-1].removesuffix(".git")
13
clone_path = f"/tmp/{repo_name}.git"
14
ssh_url = repo_url.replace("https://gitee.com/", "git@gitee.com:")
15
16
17
subprocess.run(
18
["git", "clone", "--bare", "--quiet", ssh_url, clone_path],
19
capture_output=True, check=True,
20
)
21
22
23
th_proc = subprocess.Popen([
24
"trufflehog", "git", "--bare", f"file://{clone_path}",
25
"--json", "--only-verified", "--allow-verification-overlap",
26
], stdout=subprocess.PIPE, text=True)
27
28
29
kf_proc = subprocess.Popen([
30
"kingfisher", "scan", clone_path,
31
"--format", "jsonl", "--only-valid",
32
33
34
], stdout=subprocess.PIPE, text=True)
35
36
37
th_output = th_proc.communicate()[0].strip()
38
kf_output = kf_proc.communicate()[0].strip()
39
40
41
... write hits to the shared findings volume
Dispatching 7.25M repos in batches
I split the corpus into 50 batches of ~150K repos and ran them sequentially at a steady 350 concurrent containers. Smaller batches were safer at this scale and easier to resume after interruptions; running larger batches was unstable and would hit clone-side blocks.
Cost and scale
Total wall time: roughly one month of continuous execution. Per-repo clone times were a major contributor: Gitee's servers sit in China, and cross-Pacific network distance plus SSH protocol overhead made each clone materially slower than an equivalent GitHub clone would be. The total cost was $3,097.64, fully absorbed by the $5,000 compute-credit sponsorship from Modal that supported this research. The same Modal + dual-scanner architecture works against most Git hosts with little more than a clone-URL substitution. I used a smaller-scale version of this approach in aprior investigation of Algolia DocSearch admin keys.
The results
Across the 7,256,531 public Gitee repositories scanned, the dual-scanner pipeline surfaced 26,400 unique, verified-live credentials after deduplicating by token value.
Both scanners authenticate each candidate against its vendor's API at scan time (TruffleHog's --only-verified and Kingfisher's --only-valid modes), discarding anything that pattern-matches but fails to authenticate. The 26,400 figure is deduplicated by raw secret value, so duplicates across repositories or scanners count once.
| Scanner | Unique Findings |
|---|---|
| TruffleHog | 12,737 |
| Kingfisher | 14,664 |
| Caught by both | 1,001 |
| Total unique credentials | 26,400 |
Either scanner alone would have caught only about half of these credentials. The two tools' detector coverage was largely complementary: TruffleHog dominated Alibaba Cloud key detection (9,428 vs. 993), while Kingfisher dominated DeepSeek (2,940 vs. 230), OpenAI (1,198 vs. 150), HuggingFace (154 vs. 1), and Chinese-AI services like Zhipu (1,489 vs. 0) that TruffleHog has no detector for.
Secrets by type
The single most common leaked credential type was Alibaba Cloud AccessKey pairs: roughly 1 in every 766 Gitee repositories contained a verified-live Alibaba Cloud credential.
Figure 2. Credentials by vendor.
A long tail of additional vendors (LangSmith, OpenWeather, Algolia, PostHog, OpenRouter, MySQL, Stripe, Slack, Twitter, and others) accounts for the rest.
Secrets committed by date
The vast majority of verified-live credentials were committed in the last two years. 2025 alone accounts for 12,242 unique credentials, nearly half the entire dataset.
Figure 3. Unique credentials committed by year.
Secrets by committer email domain
Most credentials were committed from personal mail services (qq.com, 163.com, gmail.com), making per-company attribution impossible at scale. The corporate-domain numbers below are a floor; even so, the names span major Chinese and Western tech companies alike.
Figure 5. Unique credentials by committer email domain.
Smaller corporate footprints also appear from Oracle, Apple, Meta, Anthropic, and others, plus Chinese commercial and academic domains.
Comparing Gitee with GitLab and Bitbucket
The closest prior data points come fromTruffleSecurity's scan of 5.6 million public GitLab repositories, and a companion Bitbucket scan referenced in that post.
| Platform | Repos scanned | Verified-live credentials | Per 1K repos |
|---|---|---|---|
| Bitbucket | ~2,600,000 | 6,212 | 2.39 |
| GitLab | ~5,600,000 | 17,430 | 3.11 |
| Gitee | 7,256,531 | 26,400 | 3.64 |
Gitee tops both Western platforms on raw count and per-repository density. One methodology note: the TruffleSecurity scans used TruffleHog alone, while this one used TruffleHog and Kingfisher together. TruffleHog's detector library skews toward Western vendors, and Gitee has heavy Chinese-vendor activity (Alibaba Cloud, DeepSeek, Zhipu, and others) that Kingfisher's coverage helps to surface; stripping our findings to TruffleHog-only puts Gitee at 1.76 per 1,000 repos.
Disclosure
I emailed the vendors with the highest exposure volume in this scan, providing each a coordinated report containing the full list of verified-live credentials associated with their service, along with source-repository metadata to help them attribute affected customers.
Per-developer notification (contacting the committer of each leaked credential individually) was not pursued at this scale. Vendor-side revocation is the more reliable path, since it doesn't require any action from the user who leaked the credential.
At the time of writing, revocation status for the disclosed credentials has not yet been confirmed.
Summary
Gitee is a meaningful credential-exposure surface. 26,400 verified-live credentials across 7.25M repositories. Per-repository density higher than comparable published scans of GitLab and Bitbucket.
Major tech companies appear in the committer metadata, Chinese and Western alike. The breadth includes Huawei, ByteDance, Google, Microsoft, Oracle, and others, suggesting this is not a purely-Chinese-developer phenomenon.
Disclosure paths are asymmetric. Western companies typically have bug bounty programs or accessible security channels. Most Chinese companies don't, leaving leaked credentials without a clear remediation path.
The architecture is portable. The same Modal + dual-scanner pipeline works on most Git hosts with little more than a clone-URL substitution; the full scan cost roughly $3,000.
share this
Author
BZ
Ben Zimmermann
Independent Security Researcher
MongoDB Resources
Documentation|MongoDB Community|MongoDB Skill Badges|Atlas Learning Hub|MongoDB Events
English
© 2026 MongoDB, Inc.
About
Support
- Contact Us
- Customer Portal
- Atlas Status
- Product Updates
- Customer Support
- Manage Cookies
- Your Privacy Choices
Build
Data Basics
- Vector Databases
- NoSQL Databases
- Document Databases
- RAG Database
- ACID Transactions
- MERN Stack
- Agent Memory
- MEAN Stack
© 2026 MongoDB, Inc.
来源:Hacker News-首页 · mongodb.com