├── requirements.txt
├── scan_reports
├── README.md
├── scan_report_20251017_032722.txt
├── scan_report_20251016_094244.txt
├── scan_report_20251017_032844.txt
├── scan_report_20251016_044713.txt
├── scan_report_20251016_015525.txt
└── scan_report_20251010_035942.txt
├── .gitignore
├── LICENSE
├── config.py
├── scan_github.py
├── README.md
├── secret_detector.py
├── scan_history.py
├── .github
└── workflows
│ ├── auto-scan.yml
│ ├── manual-scan.yml
│ └── scheduled-scan.yml
├── github_scanner.py
├── scanner.py
├── report_generator.py
└── scan_history
└── scanned_repos.json
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests==2.31.0
2 | PyGithub==2.1.1
3 | python-dotenv==1.0.0
--------------------------------------------------------------------------------
/scan_reports/README.md:
--------------------------------------------------------------------------------
1 | # 扫描报告
2 |
3 | 此目录包含 AI API Key Scanner 生成的扫描报告。
4 |
5 | ⚠️ **注意**:fork 我的仓库以后把你的 仓库设置为为私有仓库,可安全存储报告。
6 |
7 | ## 报告命名规范
8 |
9 | 报告文件采用以下命名格式:
10 | `scan_report_YYYYMMDD_HHMMSS.txt`
11 |
12 | 示例:`scan_report_20251009_143000.txt`
13 |
14 | ## 报告保留策略
15 |
16 | - 保留近期报告用于历史追踪
17 | - 定期清理旧报告以节省空间
18 | - 重要发现应立即处理
19 |
20 | ## 查看报告
21 |
22 | 可以直接在 GitHub 上查看报告,或下载到本地。
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.so
6 | .Python
7 | build/
8 | develop-eggs/
9 | dist/
10 | downloads/
11 | eggs/
12 | .eggs/
13 | lib/
14 | lib64/
15 | parts/
16 | sdist/
17 | var/
18 | wheels/
19 | *.egg-info/
20 | .installed.cfg
21 | *.egg
22 |
23 | # Virtual Environment
24 | venv/
25 | env/
26 | ENV/
27 |
28 | # IDE
29 | .vscode/
30 | .idea/
31 | *.swp
32 | *.swo
33 | *~
34 |
35 | # Environment variables
36 | .env
37 |
38 | # Scan reports - 现在提交到仓库(私密仓库安全)
39 | # scan_reports/ - 不再忽略,需要提交到仓库
40 | # scan_reports/*.txt - 不再忽略
41 |
42 | # Scan history (keep this in repo for tracking)
43 | # scan_history/ - 不要忽略,需要提交到仓库
44 |
45 | # Logs
46 | *.log
47 |
48 | # OS
49 | .DS_Store
50 | Thumbs.db
51 |
52 | # Temporary files
53 | temp_repos/
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 InCloud Security Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | ---
24 |
25 | DISCLAIMER:
26 | This tool is provided for security research and legitimate security auditing
27 | purposes only. Users are responsible for ensuring their use of this tool
28 | complies with all applicable laws and regulations. The authors assume no
29 | liability for any misuse of this tool.
30 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | """
2 | 配置文件 - InCloud GitHub 云上扫描器
3 | """
4 | import os
5 | from dotenv import load_dotenv
6 |
7 | # 加载环境变量
8 | load_dotenv()
9 |
10 | # GitHub配置
11 | GITHUB_TOKEN = os.getenv('GITHUB_TOKEN', '')
12 |
13 | # 扫描配置
14 | SCAN_INTERVAL_HOURS = int(os.getenv('SCAN_INTERVAL_HOURS', 24))
15 | OUTPUT_DIR = os.getenv('OUTPUT_DIR', './scan_reports')
16 |
17 | # AI相关的敏感信息模式
18 | SENSITIVE_PATTERNS = [
19 | # OpenAI API密钥格式
20 | r'sk-[a-zA-Z0-9]{32,}',
21 | r'sk-proj-[a-zA-Z0-9_-]{32,}',
22 |
23 | # Anthropic API密钥格式
24 | r'sk-ant-[a-zA-Z0-9_-]{32,}',
25 |
26 | # Google AI (Gemini) API密钥格式
27 | r'AIza[a-zA-Z0-9_-]{35}',
28 |
29 | # ===== 常见环境变量名模式 (snake_case) =====
30 | # AI API Keys
31 | r'AI_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
32 | r'ai_api_key[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
33 |
34 | # OpenAI
35 | r'OPENAI_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
36 | r'openai_api_key[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
37 | r'OPENAI_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
38 |
39 | # Anthropic
40 | r'ANTHROPIC_AUTH_TOKEN[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
41 | r'ANTHROPIC_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
42 | r'anthropic_api_key[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
43 |
44 | # Claude
45 | r'CLAUDE_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
46 | r'claude_api_key[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
47 |
48 | # 通用 API Key
49 | r'API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
50 | r'api_key[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
51 |
52 | # Chat API Key
53 | r'CHAT_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
54 | r'chat_api_key[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
55 |
56 | # ===== camelCase 和 PascalCase 模式 =====
57 | # 对象属性赋值: apiKey: "value"
58 | r'apiKey[\s]*:[\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
59 | r'ApiKey[\s]*:[\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
60 |
61 | # 变量赋值: apiKey = "value"
62 | r'apiKey[\s]*=[\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
63 | r'ApiKey[\s]*=[\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
64 |
65 | # chatApiKey 模式
66 | r'chatApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
67 | r'ChatApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
68 |
69 | # openaiApiKey 模式
70 | r'openaiApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
71 | r'OpenaiApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
72 | r'openAIKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
73 |
74 | # anthropicApiKey 模式
75 | r'anthropicApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
76 | r'AnthropicApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
77 |
78 | # ===== 其他 AI 服务 =====
79 | # Google AI / Gemini
80 | r'GOOGLE_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
81 | r'GEMINI_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
82 |
83 | # Hugging Face
84 | r'HUGGINGFACE_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
85 | r'HF_TOKEN[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
86 |
87 | # Cohere
88 | r'COHERE_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
89 |
90 | # Azure OpenAI
91 | r'AZURE_OPENAI_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
92 | r'AZURE_OPENAI_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
93 | ]
94 |
95 | # GitHub搜索关键词
96 | AI_SEARCH_KEYWORDS = [
97 | 'openai api',
98 | 'anthropic claude',
99 | 'gpt api',
100 | 'AI_API_KEY',
101 | 'ANTHROPIC_AUTH_TOKEN',
102 | 'chat_api_key',
103 | 'apiKey',
104 | 'sk-ant-',
105 | 'sk-proj-',
106 | 'OPENAI_API_KEY',
107 | 'chatApiKey',
108 | ]
109 |
110 | # 要排除的文件扩展名
111 | EXCLUDED_EXTENSIONS = [
112 | '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg',
113 | '.mp4', '.avi', '.mov', '.wmv',
114 | '.zip', '.tar', '.gz', '.rar',
115 | '.exe', '.dll', '.so', '.dylib',
116 | '.pdf', '.doc', '.docx',
117 | ]
118 |
119 | # 要排除的目录
120 | EXCLUDED_DIRS = [
121 | 'node_modules',
122 | '.git',
123 | 'dist',
124 | 'build',
125 | '__pycache__',
126 | 'venv',
127 | 'env',
128 | ]
129 |
130 | # GitHub API速率限制
131 | MAX_REPOS_PER_SEARCH = 100
132 | SEARCH_DELAY_SECONDS = 2
133 |
--------------------------------------------------------------------------------
/scan_github.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | InCloud GitHub 云上扫描器 - 主程序
4 | 用于扫描GitHub仓库中泄露的AI API密钥和敏感信息
5 | """
6 | import argparse
7 | import sys
8 | import os
9 | from datetime import datetime
10 | from config import GITHUB_TOKEN
11 | from scanner import CloudScanner
12 |
13 |
14 | def print_banner():
15 | """打印程序横幅"""
16 | banner = """
17 | ╔═══════════════════════════════════════════════════════════╗
18 | ║ ║
19 | ║ InCloud GitHub 云上扫描器 ║
20 | ║ AI API Key Leakage Scanner ║
21 | ║ ║
22 | ║ Version: 1.0.0 ║
23 | ║ ║
24 | ╚═══════════════════════════════════════════════════════════╝
25 | """
26 | print(banner)
27 |
28 |
29 | def validate_github_token() -> bool:
30 | """验证GitHub Token是否存在"""
31 | if not GITHUB_TOKEN:
32 | print("❌ 错误: 未找到 GitHub Token")
33 | print("\n请按以下步骤设置:")
34 | print("1. 复制 .env.example 为 .env")
35 | print("2. 在 https://github.com/settings/tokens 创建 Personal Access Token")
36 | print("3. 将 Token 添加到 .env 文件中的 GITHUB_TOKEN 变量")
37 | return False
38 | return True
39 |
40 |
41 | def main():
42 | """主函数"""
43 | print_banner()
44 |
45 | # 创建命令行参数解析器
46 | parser = argparse.ArgumentParser(
47 | description='扫描 GitHub 仓库中泄露的 AI API 密钥和敏感信息',
48 | formatter_class=argparse.RawDescriptionHelpFormatter,
49 | epilog="""
50 | 使用示例:
51 | # 扫描指定用户的所有公开仓库
52 | python scan_github.py --user username
53 |
54 | # 扫描指定组织的所有公开仓库
55 | python scan_github.py --org organization_name
56 |
57 | # 扫描单个仓库
58 | python scan_github.py --repo owner/repo_name
59 |
60 | # 自动搜索并扫描 AI 相关项目
61 | python scan_github.py --auto
62 |
63 | # 自动搜索并扫描指定数量的仓库
64 | python scan_github.py --auto --max-repos 100
65 | """
66 | )
67 |
68 | # 添加参数
69 | parser.add_argument(
70 | '--user',
71 | type=str,
72 | help='扫描指定 GitHub 用户的所有公开仓库'
73 | )
74 |
75 | parser.add_argument(
76 | '--org',
77 | type=str,
78 | help='扫描指定 GitHub 组织的所有公开仓库'
79 | )
80 |
81 | parser.add_argument(
82 | '--repo',
83 | type=str,
84 | help='扫描单个仓库 (格式: owner/repo_name)'
85 | )
86 |
87 | parser.add_argument(
88 | '--auto',
89 | action='store_true',
90 | help='自动搜索并扫描 AI 相关项目'
91 | )
92 |
93 | parser.add_argument(
94 | '--max-repos',
95 | type=int,
96 | default=50,
97 | help='自动模式下最大扫描仓库数 (默认: 50)'
98 | )
99 |
100 | parser.add_argument(
101 | '--token',
102 | type=str,
103 | help='GitHub Personal Access Token (可选,默认从 .env 读取)'
104 | )
105 |
106 | parser.add_argument(
107 | '--output-dir',
108 | type=str,
109 | help='报告输出目录 (可选,默认: ./scan_reports)'
110 | )
111 |
112 | parser.add_argument(
113 | '--no-skip-scanned',
114 | action='store_true',
115 | help='不跳过已扫描的仓库,强制重新扫描所有仓库'
116 | )
117 |
118 | # 解析参数
119 | args = parser.parse_args()
120 |
121 | # 检查是否提供了至少一个扫描选项
122 | if not any([args.user, args.org, args.repo, args.auto]):
123 | parser.print_help()
124 | print("\n❌ 错误: 请至少指定一个扫描选项 (--user, --org, --repo, 或 --auto)")
125 | sys.exit(1)
126 |
127 | # 验证 GitHub Token
128 | token = args.token or GITHUB_TOKEN
129 | if not token:
130 | if not validate_github_token():
131 | sys.exit(1)
132 |
133 | # 设置输出目录
134 | if args.output_dir:
135 | os.environ['OUTPUT_DIR'] = args.output_dir
136 |
137 | try:
138 | # 创建扫描器实例
139 | skip_scanned = not args.no_skip_scanned
140 | scanner = CloudScanner(token, skip_scanned=skip_scanned)
141 |
142 | # 根据参数执行不同的扫描
143 | if args.user:
144 | report_path = scanner.scan_user(args.user)
145 | elif args.org:
146 | report_path = scanner.scan_organization(args.org)
147 | elif args.repo:
148 | report_path = scanner.scan_single_repo(args.repo)
149 | elif args.auto:
150 | report_path = scanner.scan_ai_projects(max_repos=args.max_repos)
151 |
152 | print(f"\n✅ 扫描完成!")
153 | print(f"📄 报告已保存至: {report_path}")
154 |
155 | except KeyboardInterrupt:
156 | print("\n\n⚠️ 用户中断扫描")
157 | sys.exit(0)
158 | except Exception as e:
159 | print(f"\n❌ 扫描过程中发生错误: {e}")
160 | import traceback
161 | traceback.print_exc()
162 | sys.exit(1)
163 |
164 |
165 | if __name__ == "__main__":
166 | main()
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🔍 AI API Key Scanner
2 |
3 |
4 |
5 | **自动扫描 GitHub 仓库,发现泄露的 AI API 密钥**
6 |
7 | [](https://www.python.org/downloads/)
8 | [](LICENSE)
9 | [](https://github.com/features/actions)
10 |
11 | 🚀 **完全基于 GitHub Actions,无需本地运行** | 💰 **公开仓库完全免费**
12 |
13 |
14 |
15 | ---
16 |
17 | ## 📖 简介
18 |
19 | 自动化安全扫描工具,发现 GitHub 仓库中泄露的 AI API 密钥(OpenAI、Anthropic Claude 等)。
20 |
21 | ### 核心特性
22 |
23 | - ✅ **零配置运行** - Fork 后只需添加 Token
24 | - ✅ **全自动化** - 每天自动扫描
25 | - ✅ **智能检测** - 自动过滤示例代码,减少误报
26 | - ✅ **详细报告** - 报告提交到仓库永久保存,同时上传 Artifacts 保留 90 天
27 | - ✅ **即时告警** - 发现问题自动创建 Issue
28 | - ✅ **完全免费** - 公开仓库无限使用
29 |
30 | ---
31 |
32 | ## 🚀 快速开始
33 |
34 | ### 1. Fork 本仓库
35 |
36 | 点击页面右上角的 **Fork** 按钮。
37 |
38 | ### 2. 创建 GitHub Token
39 |
40 | 1. 访问 https://github.com/settings/tokens
41 | 2. 点击 **Generate new token (classic)**
42 | 3. 配置:
43 | - Note: `AI Scanner Token`
44 | - Expiration: `90 days` 或更长
45 | - Scopes: ✅ `public_repo` (必需)、✅ `read:org` (可选)
46 | 4. 生成并复制 Token(格式:`ghp_xxxxxxxxxxxx`)
47 |
48 | ### 3. 添加 Token 到仓库
49 |
50 | 1. 访问你 Fork 的仓库:`Settings` → `Secrets and variables` → `Actions`
51 | 2. 点击 **New repository secret**
52 | 3. Name: `GH_SCAN_TOKEN`(必须大小写一致)
53 | 4. Value: 粘贴你的 Token
54 | 5. 点击 **Add secret**
55 |
56 | ### 4. 启动扫描
57 |
58 | **手动触发**(推荐首次使用):
59 | 1. 访问 `Actions` 页面
60 | 2. 选择 **AI API Key Scanner - Manual Scan**
61 | 3. 点击 **Run workflow**
62 | 4. 配置参数:
63 | - Scan type: `auto - 自动搜索AI项目`
64 | - Max repos: `10`(首次建议少量测试)
65 | 5. 点击 **Run workflow** 开始扫描
66 |
67 | **自动运行**:配置完成后,工作流将每天 UTC 02:00(北京时间 10:00)自动运行。
68 |
69 | ---
70 |
71 | ## 📊 查看结果
72 |
73 | ### 1. 查看仓库中的报告
74 | 扫描完成后,报告会自动提交到 `scan_reports/` 目录,可以直接在仓库中查看。
75 |
76 | ### 2. 查看运行日志
77 | 在 Actions 页面点击任意运行记录查看扫描状态和摘要。
78 |
79 | ### 3. 下载 Artifacts
80 | 运行详情页面底部 **Artifacts** 区域可下载报告副本(保留 90 天)。
81 |
82 | ### 4. 查看自动 Issue
83 | 发现问题时会自动创建带 `security` 标签的 Issue。
84 |
85 | ---
86 |
87 | ## 🎯 功能特性
88 |
89 | ### 智能检测
90 | - 支持多种 AI API 密钥:OpenAI (`sk-...`、`sk-proj-...`)、Anthropic (`sk-ant-...`)
91 | - 自动过滤示例代码和占位符
92 | - 置信度评分(高/中/低)
93 |
94 | ### 详细报告
95 | - 包含仓库地址、文件路径、行号、代码片段
96 | - 部分隐藏密钥保护安全
97 | - 统计分析和安全建议
98 | - 报告保存在 `scan_reports/` 目录,提交到仓库永久保存
99 |
100 | ### 自动告警
101 | - 发现问题自动创建 Issue
102 | - 智能避免重复 Issue
103 |
104 | ### 灵活配置
105 | - 4 种扫描模式:auto(自动搜索AI项目)、user(指定用户)、org(指定组织)、repo(单个仓库)
106 | - 自定义扫描数量和时间
107 |
108 | ---
109 |
110 | ## 🛠️ 工作流说明
111 |
112 | ### Manual Scan(手动扫描)⭐ 推荐
113 | - 触发方式:仅手动触发
114 | - 适用场景:首次测试、按需扫描
115 |
116 | ### Auto Scan(自动扫描)
117 | - 触发方式:每天 UTC 02:00 或手动触发
118 | - 适用场景:日常自动监控
119 |
120 | ### Scheduled Scan(增强定时扫描)
121 | - 触发方式:每天 UTC 02:00 或手动触发
122 | - 适用场景:企业级定期安全审计
123 | - 特点:详细统计分析、智能 Issue 管理、超时保护(60 分钟)
124 |
125 | ---
126 |
127 | ## ⚙️ 自定义配置
128 |
129 | ### 修改扫描时间
130 |
131 | 编辑 `.github/workflows/scheduled-scan.yml`:
132 |
133 | ```yaml
134 | on:
135 | schedule:
136 | - cron: '0 2 * * *' # 每天 02:00 UTC(北京时间 10:00)
137 | # - cron: '0 */12 * * *' # 每 12 小时
138 | # - cron: '0 2 * * 1' # 每周一
139 | ```
140 |
141 | **时区说明**:GitHub Actions 使用 UTC 时区,北京时间 = UTC + 8 小时。
142 |
143 | ### 修改扫描数量
144 |
145 | 编辑工作流文件:
146 |
147 | ```yaml
148 | python scan_github.py --auto --max-repos 100 # 修改数字
149 | ```
150 |
151 | ### 添加自定义检测规则
152 |
153 | 编辑 `config.py`:
154 |
155 | ```python
156 | SENSITIVE_PATTERNS = [
157 | r'sk-[a-zA-Z0-9]{32,}', # OpenAI
158 | r'sk-ant-[a-zA-Z0-9_-]{32,}', # Anthropic
159 | r'your_custom_pattern', # 自定义规则
160 | ]
161 | ```
162 |
163 | ---
164 |
165 | ## ❓ 常见问题
166 |
167 | **Q: 工作流没有自动运行?**
168 | - 检查 Actions 是否启用
169 | - 手动触发一次测试
170 | - 确认 Cron 时间设置正确
171 |
172 | **Q: 提示 "GitHub Token is required"?**
173 | - 确认 Secret 名称是 `GH_SCAN_TOKEN`(大小写一致)
174 | - 检查 Token 是否过期
175 | - 验证 Token 有 `public_repo` 权限
176 |
177 | **Q: API 速率限制?**
178 | - 减少 `--max-repos` 参数
179 | - 增加扫描间隔
180 | - 等待 1 小时后重试
181 |
182 | **Q: 如何停止自动扫描?**
183 | - Actions 页面 → 选择工作流 → "..." → "Disable workflow"
184 |
185 | ---
186 |
187 | ## 🛡️ 发现泄露密钥后的处理
188 |
189 | ### 立即采取的步骤
190 |
191 | 1. **轮换密钥**:登录 API 提供商控制台,删除泄露的密钥,生成新密钥
192 | 2. **检查日志**:查看 API 调用记录,确认是否有异常使用
193 | 3. **清理 Git 历史**:使用 BFG Repo-Cleaner 或 git-filter-repo 清理
194 | 4. **更新代码实践**:
195 |
196 | ```python
197 | # ✅ 推荐:使用环境变量
198 | import os
199 | api_key = os.getenv('OPENAI_API_KEY')
200 |
201 | # ❌ 错误:硬编码密钥
202 | api_key = "sk-proj-xxxxxxxxxxxx"
203 | ```
204 |
205 | 更新 `.gitignore`:
206 |
207 | ```gitignore
208 | .env
209 | .env.local
210 | config.json
211 | secrets.json
212 | *.key
213 | *.pem
214 | ```
215 |
216 | ---
217 |
218 | ## ⚠️ 免责声明
219 |
220 | - 本工具仅用于安全研究和合法的安全审计
221 | - 用户需对使用本工具的行为负责
222 | - 请遵守相关法律法规和 GitHub 使用条款
223 | - 工具可能存在误报或漏报,建议人工复核
224 |
225 | ---
226 |
227 | ## 📄 许可证
228 |
229 | 本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。
230 |
231 | ---
232 |
233 |
234 |
235 | ### 🛡️ 让你的代码更安全!
236 |
237 | Made with ❤️ for Security
238 |
239 |
240 |
--------------------------------------------------------------------------------
/secret_detector.py:
--------------------------------------------------------------------------------
1 | """
2 | 敏感信息检测模块
3 | """
4 | import re
5 | from typing import List, Dict, Optional
6 | from config import SENSITIVE_PATTERNS, EXCLUDED_EXTENSIONS, EXCLUDED_DIRS
7 |
8 |
9 | class SecretDetector:
10 | """敏感信息检测器"""
11 |
12 | def __init__(self, patterns: List[str] = SENSITIVE_PATTERNS):
13 | """
14 | 初始化检测器
15 |
16 | Args:
17 | patterns: 正则表达式模式列表
18 | """
19 | self.patterns = [re.compile(pattern) for pattern in patterns]
20 | self.excluded_extensions = EXCLUDED_EXTENSIONS
21 | self.excluded_dirs = EXCLUDED_DIRS
22 |
23 | def should_scan_file(self, file_path: str) -> bool:
24 | """
25 | 判断文件是否应该被扫描
26 |
27 | Args:
28 | file_path: 文件路径
29 |
30 | Returns:
31 | 是否应该扫描
32 | """
33 | # 检查文件扩展名
34 | for ext in self.excluded_extensions:
35 | if file_path.lower().endswith(ext):
36 | return False
37 |
38 | # 检查目录
39 | path_parts = file_path.split('/')
40 | for excluded_dir in self.excluded_dirs:
41 | if excluded_dir in path_parts:
42 | return False
43 |
44 | return True
45 |
46 | def detect_secrets_in_text(self, text: str, file_path: str = "") -> List[Dict]:
47 | """
48 | 在文本中检测敏感信息
49 |
50 | Args:
51 | text: 要检测的文本内容
52 | file_path: 文件路径(用于报告)
53 |
54 | Returns:
55 | 检测到的敏感信息列表
56 | """
57 | if not text:
58 | return []
59 |
60 | findings = []
61 | lines = text.split('\n')
62 |
63 | for line_num, line in enumerate(lines, 1):
64 | for pattern in self.patterns:
65 | matches = pattern.finditer(line)
66 | for match in matches:
67 | # 提取匹配的密钥
68 | secret = match.group(0)
69 |
70 | # 检查是否是注释或示例
71 | if self._is_likely_example(line, secret):
72 | continue
73 |
74 | findings.append({
75 | 'file_path': file_path,
76 | 'line_number': line_num,
77 | 'line_content': line.strip(),
78 | 'secret': secret,
79 | 'pattern': pattern.pattern,
80 | 'confidence': self._calculate_confidence(secret, line)
81 | })
82 |
83 | return findings
84 |
85 | def _is_likely_example(self, line: str, secret: str) -> bool:
86 | """
87 | 判断是否可能是示例代码
88 |
89 | Args:
90 | line: 代码行
91 | secret: 检测到的密钥
92 |
93 | Returns:
94 | 是否可能是示例
95 | """
96 | line_lower = line.lower()
97 |
98 | # 检查是否包含示例相关的关键词
99 | example_keywords = [
100 | 'example', 'sample', 'demo', 'test', 'placeholder',
101 | 'your_api_key', 'your-api-key', 'xxx', 'yyy',
102 | 'todo', 'replace', 'change_me', 'changeme'
103 | ]
104 |
105 | for keyword in example_keywords:
106 | if keyword in line_lower:
107 | return True
108 |
109 | # 检查密钥是否包含明显的占位符模式
110 | placeholder_patterns = [
111 | r'x{10,}', # 多个x
112 | r'_+', # 多个下划线
113 | r'\*{3,}', # 多个星号
114 | ]
115 |
116 | for pattern in placeholder_patterns:
117 | if re.search(pattern, secret, re.IGNORECASE):
118 | return True
119 |
120 | return False
121 |
122 | def _calculate_confidence(self, secret: str, line: str) -> str:
123 | """
124 | 计算置信度
125 |
126 | Args:
127 | secret: 检测到的密钥
128 | line: 代码行
129 |
130 | Returns:
131 | 置信度等级 (high/medium/low)
132 | """
133 | # 高置信度:密钥格式完整且不在注释中
134 | if (secret.startswith('sk-') and len(secret) > 40 and
135 | not line.strip().startswith('#') and
136 | not line.strip().startswith('//')):
137 | return 'high'
138 |
139 | # 中等置信度:符合基本模式
140 | if len(secret) >= 30:
141 | return 'medium'
142 |
143 | # 低置信度
144 | return 'low'
145 |
146 | def filter_high_confidence(self, findings: List[Dict]) -> List[Dict]:
147 | """
148 | 过滤出高置信度的发现
149 |
150 | Args:
151 | findings: 检测结果列表
152 |
153 | Returns:
154 | 高置信度的结果
155 | """
156 | return [f for f in findings if f['confidence'] in ['high', 'medium']]
157 |
158 | def deduplicate_findings(self, findings: List[Dict]) -> List[Dict]:
159 | """
160 | 去除重复的发现
161 |
162 | Args:
163 | findings: 检测结果列表
164 |
165 | Returns:
166 | 去重后的结果
167 | """
168 | seen = set()
169 | unique_findings = []
170 |
171 | for finding in findings:
172 | # 使用secret和file_path作为唯一标识
173 | key = (finding['secret'], finding['file_path'])
174 | if key not in seen:
175 | seen.add(key)
176 | unique_findings.append(finding)
177 |
178 | return unique_findings
179 |
--------------------------------------------------------------------------------
/scan_history.py:
--------------------------------------------------------------------------------
1 | """
2 | 扫描历史管理模块 - 跟踪已扫描的仓库,避免重复扫描
3 | """
4 | import json
5 | import os
6 | from datetime import datetime
7 | from typing import Dict, List, Set
8 | from pathlib import Path
9 |
10 |
11 | class ScanHistory:
12 | """扫描历史管理器"""
13 |
14 | def __init__(self, history_file: str = None):
15 | """
16 | 初始化扫描历史管理器
17 |
18 | Args:
19 | history_file: 历史记录文件路径,默认为 scan_history/scanned_repos.json
20 | """
21 | if history_file is None:
22 | history_dir = Path("scan_history")
23 | history_dir.mkdir(exist_ok=True)
24 | self.history_file = history_dir / "scanned_repos.json"
25 | else:
26 | self.history_file = Path(history_file)
27 | self.history_file.parent.mkdir(exist_ok=True, parents=True)
28 |
29 | self.history = self._load_history()
30 |
31 | def _load_history(self) -> Dict:
32 | """
33 | 从文件加载扫描历史
34 |
35 | Returns:
36 | 历史记录字典
37 | """
38 | if self.history_file.exists():
39 | try:
40 | with open(self.history_file, 'r', encoding='utf-8') as f:
41 | return json.load(f)
42 | except Exception as e:
43 | print(f"⚠️ 加载扫描历史失败: {e},将创建新历史记录")
44 | return {"repos": {}, "total_scanned": 0, "last_updated": None}
45 | else:
46 | return {"repos": {}, "total_scanned": 0, "last_updated": None}
47 |
48 | def _save_history(self):
49 | """保存扫描历史到文件"""
50 | try:
51 | with open(self.history_file, 'w', encoding='utf-8') as f:
52 | json.dump(self.history, f, indent=2, ensure_ascii=False)
53 | except Exception as e:
54 | print(f"⚠️ 保存扫描历史失败: {e}")
55 |
56 | def is_scanned(self, repo_full_name: str) -> bool:
57 | """
58 | 检查仓库是否已经被扫描过
59 |
60 | Args:
61 | repo_full_name: 仓库全名 (owner/repo)
62 |
63 | Returns:
64 | True 如果已扫描,False 如果未扫描
65 | """
66 | return repo_full_name in self.history["repos"]
67 |
68 | def get_scan_info(self, repo_full_name: str) -> Dict:
69 | """
70 | 获取仓库的扫描信息
71 |
72 | Args:
73 | repo_full_name: 仓库全名 (owner/repo)
74 |
75 | Returns:
76 | 扫描信息字典,如果未扫描过则返回 None
77 | """
78 | return self.history["repos"].get(repo_full_name)
79 |
80 | def mark_as_scanned(self, repo_full_name: str, findings_count: int = 0,
81 | scan_type: str = "unknown"):
82 | """
83 | 标记仓库为已扫描
84 |
85 | Args:
86 | repo_full_name: 仓库全名 (owner/repo)
87 | findings_count: 发现的问题数量
88 | scan_type: 扫描类型
89 | """
90 | self.history["repos"][repo_full_name] = {
91 | "first_scan": self.history["repos"].get(repo_full_name, {}).get(
92 | "first_scan",
93 | datetime.now().strftime('%Y-%m-%d %H:%M:%S')
94 | ),
95 | "last_scan": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
96 | "findings_count": findings_count,
97 | "scan_type": scan_type,
98 | "scan_count": self.history["repos"].get(repo_full_name, {}).get("scan_count", 0) + 1
99 | }
100 |
101 | self.history["total_scanned"] = len(self.history["repos"])
102 | self.history["last_updated"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
103 |
104 | self._save_history()
105 |
106 | def get_scanned_repos(self) -> List[str]:
107 | """
108 | 获取所有已扫描的仓库列表
109 |
110 | Returns:
111 | 仓库全名列表
112 | """
113 | return list(self.history["repos"].keys())
114 |
115 | def get_scanned_count(self) -> int:
116 | """
117 | 获取已扫描的仓库总数
118 |
119 | Returns:
120 | 仓库数量
121 | """
122 | return self.history["total_scanned"]
123 |
124 | def clear_history(self):
125 | """清空扫描历史"""
126 | self.history = {"repos": {}, "total_scanned": 0, "last_updated": None}
127 | self._save_history()
128 | print("✅ 扫描历史已清空")
129 |
130 | def remove_repo(self, repo_full_name: str):
131 | """
132 | 从历史记录中移除指定仓库
133 |
134 | Args:
135 | repo_full_name: 仓库全名 (owner/repo)
136 | """
137 | if repo_full_name in self.history["repos"]:
138 | del self.history["repos"][repo_full_name]
139 | self.history["total_scanned"] = len(self.history["repos"])
140 | self.history["last_updated"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
141 | self._save_history()
142 | print(f"✅ 已从历史记录中移除: {repo_full_name}")
143 | else:
144 | print(f"⚠️ 仓库不在历史记录中: {repo_full_name}")
145 |
146 | def get_statistics(self) -> Dict:
147 | """
148 | 获取扫描统计信息
149 |
150 | Returns:
151 | 统计信息字典
152 | """
153 | total_findings = sum(
154 | repo_info.get("findings_count", 0)
155 | for repo_info in self.history["repos"].values()
156 | )
157 |
158 | repos_with_findings = sum(
159 | 1 for repo_info in self.history["repos"].values()
160 | if repo_info.get("findings_count", 0) > 0
161 | )
162 |
163 | return {
164 | "total_scanned": self.history["total_scanned"],
165 | "total_findings": total_findings,
166 | "repos_with_findings": repos_with_findings,
167 | "last_updated": self.history["last_updated"]
168 | }
169 |
170 | def print_statistics(self):
171 | """打印扫描统计信息"""
172 | stats = self.get_statistics()
173 | print(f"\n📊 扫描历史统计:")
174 | print(f" 总扫描仓库数: {stats['total_scanned']}")
175 | print(f" 发现问题总数: {stats['total_findings']}")
176 | print(f" 有问题的仓库: {stats['repos_with_findings']}")
177 | if stats['last_updated']:
178 | print(f" 最后更新时间: {stats['last_updated']}")
179 |
180 |
--------------------------------------------------------------------------------
/scan_reports/scan_report_20251017_032722.txt:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════════════════════════════════════╗
2 | ║ ║
3 | ║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
4 | ║ ║
5 | ╚══════════════════════════════════════════════════════════════════════════════╝
6 |
7 | 📋 扫描信息
8 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9 | 🎯 扫描类型: 🤖 自动搜索 AI 项目
10 | ⏱️ 开始时间: 2025-10-17 03:05:32
11 | ⏱️ 结束时间: 2025-10-17 03:27:22
12 | ⏳ 扫描耗时: 21分49秒
13 | 🔴 发现问题数: 4 个 (🔴 3 高危, 🟡 1 中危)
14 | 📦 涉及仓库数: 2 个
15 |
16 |
17 | ╭──────────────────────────────────────────────────────────────────────────────╮
18 | │ 📦 仓库: Sasmitha6821/Kalix-Chatbot │
19 | │ 🔗 地址: https://github.com/Sasmitha6821/Kalix-Chatbot │
20 | │ 🔴 高危 发现 3 个问题 │
21 | ╰──────────────────────────────────────────────────────────────────────────────╯
22 |
23 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
24 | │
25 | │ 🔴 风险等级: 高危 - 立即处理
26 | │
27 | │ 📄 文件路径: Applied Project 2/ChatBot/API.py
28 | │ 📍 行号: 4
29 | │
30 | │ 🔑 密钥类型: 🤖 OpenAI API Key
31 | │ 🔐 密钥内容: sk-a*******************************************MfKE
32 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
33 | │
34 | │ 💻 代码片段:
35 | │ openai.api_key = "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"
36 | │
37 | │ 🕐 发现时间: 2025-10-17 03:08:32
38 | │
39 | └──────────────────────────────────────────────────────────────────────────
40 |
41 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
42 | │
43 | │ 🔴 风险等级: 高危 - 立即处理
44 | │
45 | │ 📄 文件路径: Applied Project 2/ChatBot/Custom.py
46 | │ 📍 行号: 4
47 | │
48 | │ 🔑 密钥类型: 🤖 OpenAI API Key
49 | │ 🔐 密钥内容: sk-a*******************************************MfKE
50 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
51 | │
52 | │ 💻 代码片段:
53 | │ openai.api_key = "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"
54 | │
55 | │ 🕐 发现时间: 2025-10-17 03:08:32
56 | │
57 | └──────────────────────────────────────────────────────────────────────────
58 |
59 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
60 | │
61 | │ 🔴 风险等级: 高危 - 立即处理
62 | │
63 | │ 📄 文件路径: Applied Project 2/ChatBot/KalixP2.py
64 | │ 📍 行号: 3
65 | │
66 | │ 🔑 密钥类型: 🤖 OpenAI API Key
67 | │ 🔐 密钥内容: sk-a*******************************************MfKE
68 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
69 | │
70 | │ 💻 代码片段:
71 | │ openai.api_key = "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"
72 | │
73 | │ 🕐 发现时间: 2025-10-17 03:08:32
74 | │
75 | └──────────────────────────────────────────────────────────────────────────
76 |
77 |
78 |
79 | ╭──────────────────────────────────────────────────────────────────────────────╮
80 | │ 📦 仓库: BitCodeHub/YappyAI │
81 | │ 🔗 地址: https://github.com/BitCodeHub/YappyAI │
82 | │ 🟡 中危 发现 1 个问题 │
83 | ╰──────────────────────────────────────────────────────────────────────────────╯
84 |
85 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
86 | │
87 | │ 🟡 风险等级: 中危 - 尽快处理
88 | │
89 | │ 📄 文件路径: app_complete_v3.py
90 | │ 📍 行号: 286
91 | │
92 | │ 🔑 密钥类型: 🔍 Google AI API Key (Gemini)
93 | │ 🔐 密钥内容: AIza*******************************N-2Y
94 | │ 🎯 匹配规则: 📌 Google AI/Gemini API Key 格式 (AIza...)
95 | │
96 | │ 💻 代码片段:
97 | │ "key": "AIzaSyB-UeDa8gvn7QiMgLRVe8PF3gFMea2N-2Y", # Free public key with limits
98 | │
99 | │ 🕐 发现时间: 2025-10-17 03:13:22
100 | │
101 | └──────────────────────────────────────────────────────────────────────────
102 |
103 |
104 |
105 | ╔══════════════════════════════════════════════════════════════════════════════╗
106 | ║ ║
107 | ║ 📊 统计信息与分析 ║
108 | ║ ║
109 | ╚══════════════════════════════════════════════════════════════════════════════╝
110 |
111 | ┌─ 风险等级分布
112 | │
113 | │ 🔴 高危问题: 3 个 ( 75.0%) ███████████████
114 | │ 🟡 中危问题: 1 个 ( 25.0%) █████
115 | │ 🟢 低危问题: 0 个 ( 0.0%)
116 | │
117 | │ 📊 总计: 4 个潜在问题
118 | └──────────────────────────────────────────────────────────────────────────────
119 |
120 | ┌─ 影响范围
121 | │
122 | │ 📦 涉及仓库: 2 个
123 | │ 📄 涉及文件: 4 个
124 | │
125 | └──────────────────────────────────────────────────────────────────────────────
126 |
127 | ┌─ 密钥类型分布
128 | │
129 | │ 🤖 OpenAI API Key: 3 个
130 | │ 🔍 Google AI API Key (Gemini): 1 个
131 | │
132 | └──────────────────────────────────────────────────────────────────────────────
133 |
134 | ╔══════════════════════════════════════════════════════════════════════════════╗
135 | ║ 🛡️ 安全建议 ║
136 | ╚══════════════════════════════════════════════════════════════════════════════╝
137 |
138 | ⚠️ 立即行动(针对高危问题):
139 | 1. 🚨 立即撤销/轮换所有泄露的 API 密钥
140 | 2. 🔍 检查 API 使用日志,确认是否被滥用
141 | 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)
142 | 4. 📧 通知相关团队成员
143 |
144 | 🔒 长期防护措施:
145 | 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)
146 | 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件
147 | 3. 🪝 配置 pre-commit hooks 防止敏感信息提交
148 | 4. 🔄 定期轮换 API 密钥
149 | 5. 👥 对团队进行安全培训
150 | 6. 📊 定期运行此扫描工具进行审查
151 |
152 | 📚 参考资源:
153 | • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning
154 | • Git 历史清理: https://github.com/newren/git-filter-repo
155 | • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
156 |
157 | ╔══════════════════════════════════════════════════════════════════════════════╗
158 | ║ ║
159 | ║ ✅ 报告生成完成 - 请及时处理发现的问题 ║
160 | ║ ║
161 | ║ 生成时间: 2025年10月17日 03:27:22 ║
162 | ║ 报告位置: ./scan_reports/scan_report_20251017_032722.txt ║
163 | ║ ║
164 | ╚══════════════════════════════════════════════════════════════════════════════╝
165 |
--------------------------------------------------------------------------------
/.github/workflows/auto-scan.yml:
--------------------------------------------------------------------------------
1 | name: AI API Key Scanner - Auto Scan
2 |
3 | # 触发条件
4 | on:
5 | # 定时触发 - 每天 UTC 时间 02:00 执行(北京时间 10:00)
6 | schedule:
7 | - cron: '0 2 * * *' # 每天执行一次
8 | # - cron: '0 */12 * * *' # 每12小时执行一次
9 | # - cron: '0 0 * * 0' # 每周日执行一次
10 |
11 | # 手动触发
12 | workflow_dispatch:
13 | inputs:
14 | scan_mode:
15 | description: '扫描模式'
16 | required: true
17 | default: 'auto'
18 | type: choice
19 | options:
20 | - auto
21 | - user
22 | - org
23 | target:
24 | description: '扫描目标(用户名或组织名,auto模式时可留空)'
25 | required: false
26 | type: string
27 | max_repos:
28 | description: '最大扫描仓库数'
29 | required: false
30 | default: '50'
31 | type: string
32 |
33 | # 添加权限设置
34 | permissions:
35 | contents: write # 允许提交代码
36 | issues: write # 允许创建 Issue
37 |
38 | # 任务定义
39 | jobs:
40 | scan:
41 | runs-on: ubuntu-latest
42 |
43 | steps:
44 | # 1. 检出代码
45 | - name: 📥 检出代码
46 | uses: actions/checkout@v4
47 |
48 | # 2. 设置 Python 环境
49 | - name: 🐍 设置 Python
50 | uses: actions/setup-python@v5
51 | with:
52 | python-version: '3.10'
53 |
54 | # 3. 安装依赖
55 | - name: 📦 安装依赖
56 | run: |
57 | python -m pip install --upgrade pip
58 | pip install -r requirements.txt
59 |
60 | # 4. 创建环境变量文件
61 | - name: ⚙️ 配置环境变量
62 | run: |
63 | echo "GITHUB_TOKEN=${{ secrets.GH_SCAN_TOKEN }}" > .env
64 | echo "OUTPUT_DIR=./scan_reports" >> .env
65 |
66 | # 5. 执行扫描(定时任务)
67 | - name: 🔍 执行自动扫描
68 | if: github.event_name == 'schedule'
69 | run: |
70 | python scan_github.py --auto --max-repos 50
71 |
72 | # 6. 执行扫描(手动触发 - auto模式)
73 | - name: 🔍 执行扫描 (auto)
74 | if: github.event_name == 'workflow_dispatch' && github.event.inputs.scan_mode == 'auto'
75 | run: |
76 | python scan_github.py --auto --max-repos ${{ github.event.inputs.max_repos }}
77 |
78 | # 7. 执行扫描(手动触发 - user模式)
79 | - name: 🔍 执行扫描 (user)
80 | if: github.event_name == 'workflow_dispatch' && github.event.inputs.scan_mode == 'user'
81 | run: |
82 | python scan_github.py --user ${{ github.event.inputs.target }}
83 |
84 | # 8. 执行扫描(手动触发 - org模式)
85 | - name: 🔍 执行扫描 (org)
86 | if: github.event_name == 'workflow_dispatch' && github.event.inputs.scan_mode == 'org'
87 | run: |
88 | python scan_github.py --org ${{ github.event.inputs.target }}
89 |
90 | # 9. 上传扫描报告
91 | - name: 📤 上传扫描报告
92 | if: always()
93 | uses: actions/upload-artifact@v4
94 | with:
95 | name: scan-report-${{ github.run_number }}
96 | path: scan_reports/
97 | retention-days: 90
98 | if-no-files-found: ignore
99 |
100 | # 10. 提交扫描历史和报告
101 | - name: 💾 提交扫描历史和报告
102 | if: always()
103 | run: |
104 | git config user.name "gaocaipeng"
105 | git config user.email "2540225402@qq.com"
106 |
107 | # 添加扫描历史
108 | git add scan_history/
109 |
110 | # 添加扫描报告
111 | if [ -d "scan_reports" ] && [ "$(ls -A scan_reports 2>/dev/null)" ]; then
112 | git add scan_reports/
113 | echo "✅ 发现扫描报告,将一并提交"
114 | fi
115 |
116 | # 检查是否有更改
117 | if git diff --cached --quiet; then
118 | echo "没有新的扫描历史或报告"
119 | else
120 | # 统计信息
121 | HISTORY_CHANGED=$(git diff --cached --name-only | grep "scan_history/" | wc -l | tr -d ' ')
122 | REPORTS_CHANGED=$(git diff --cached --name-only | grep "scan_reports/" | wc -l | tr -d ' ')
123 |
124 | # 提交消息
125 | COMMIT_MSG="📝 更新扫描数据 [skip ci]"
126 | if [ "$HISTORY_CHANGED" -gt 0 ] && [ "$REPORTS_CHANGED" -gt 0 ]; then
127 | COMMIT_MSG="📝 更新扫描历史和报告 [skip ci]"
128 | elif [ "$REPORTS_CHANGED" -gt 0 ]; then
129 | COMMIT_MSG="📄 添加扫描报告 [skip ci]"
130 | fi
131 |
132 | git commit -m "$COMMIT_MSG"
133 |
134 | # 同步远程更改,避免并发推送冲突
135 | git pull --rebase origin main || {
136 | echo "⚠️ Rebase 冲突,尝试使用本地版本"
137 | git rebase --abort
138 | git pull --rebase --strategy-option=ours origin main
139 | }
140 |
141 | git push
142 | echo "✅ 已提交: 历史文件 $HISTORY_CHANGED 个, 报告文件 $REPORTS_CHANGED 个"
143 | fi
144 |
145 | # 11. 显示报告摘要
146 | - name: 📊 显示扫描摘要
147 | if: always()
148 | run: |
149 | if [ -d "scan_reports" ] && [ "$(ls -A scan_reports)" ]; then
150 | echo "## 📋 扫描报告生成成功" >> $GITHUB_STEP_SUMMARY
151 | echo "" >> $GITHUB_STEP_SUMMARY
152 | echo "### 📁 报告文件:" >> $GITHUB_STEP_SUMMARY
153 | ls -lh scan_reports/ >> $GITHUB_STEP_SUMMARY
154 | echo "" >> $GITHUB_STEP_SUMMARY
155 | echo "### 📄 报告预览(前50行):" >> $GITHUB_STEP_SUMMARY
156 | echo '```' >> $GITHUB_STEP_SUMMARY
157 | head -n 50 scan_reports/*.txt | tail -n 50 >> $GITHUB_STEP_SUMMARY || echo "无法读取报告内容" >> $GITHUB_STEP_SUMMARY
158 | echo '```' >> $GITHUB_STEP_SUMMARY
159 | else
160 | echo "⚠️ 未生成报告文件" >> $GITHUB_STEP_SUMMARY
161 | fi
162 |
163 | # 11. 可选:发送通知(如果发现问题)
164 | - name: 🔔 检查是否发现问题
165 | id: check_findings
166 | run: |
167 | if grep -q "发现的问题总数: [1-9]" scan_reports/*.txt 2>/dev/null; then
168 | echo "有发现问题"
169 | echo "has_findings=true" >> $GITHUB_OUTPUT
170 | else
171 | echo "未发现问题"
172 | echo "has_findings=false" >> $GITHUB_OUTPUT
173 | fi
174 |
175 | # 12. 可选:创建 Issue(如果发现高危问题)
176 | - name: 📢 创建告警 Issue
177 | if: steps.check_findings.outputs.has_findings == 'true'
178 | uses: actions/github-script@v7
179 | with:
180 | script: |
181 | const fs = require('fs');
182 | const reportFiles = fs.readdirSync('scan_reports');
183 | if (reportFiles.length > 0) {
184 | const reportContent = fs.readFileSync(`scan_reports/${reportFiles[0]}`, 'utf8');
185 | const lines = reportContent.split('\n').slice(0, 100).join('\n');
186 |
187 | github.rest.issues.create({
188 | owner: context.repo.owner,
189 | repo: context.repo.repo,
190 | title: `⚠️ 安全扫描发现潜在密钥泄露 - ${new Date().toISOString().split('T')[0]}`,
191 | body: `# 🔍 自动扫描报告\n\n本次扫描发现潜在的 API 密钥泄露问题。\n\n**扫描时间**: ${new Date().toISOString()}\n**运行ID**: #${context.runNumber}\n\n## 📄 报告摘要\n\n\`\`\`\n${lines}\n\`\`\`\n\n完整报告请查看 [Artifacts](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`,
192 | labels: ['security', 'auto-scan']
193 | });
194 | }
195 |
196 |
--------------------------------------------------------------------------------
/scan_reports/scan_report_20251016_094244.txt:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════════════════════════════════════╗
2 | ║ ║
3 | ║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
4 | ║ ║
5 | ╚══════════════════════════════════════════════════════════════════════════════╝
6 |
7 | 📋 扫描信息
8 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9 | 🎯 扫描类型: 🤖 自动搜索 AI 项目
10 | ⏱️ 开始时间: 2025-10-16 09:09:37
11 | ⏱️ 结束时间: 2025-10-16 09:42:44
12 | ⏳ 扫描耗时: 33分7秒
13 | 🔴 发现问题数: 5 个 (🔴 2 高危, 🟡 3 中危)
14 | 📦 涉及仓库数: 3 个
15 |
16 |
17 | ╭──────────────────────────────────────────────────────────────────────────────╮
18 | │ 📦 仓库: chanleeip/python_anywhere │
19 | │ 🔗 地址: https://github.com/chanleeip/python_anywhere │
20 | │ 🔴 高危 发现 2 个问题 │
21 | ╰──────────────────────────────────────────────────────────────────────────────╯
22 |
23 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
24 | │
25 | │ 🔴 风险等级: 高危 - 立即处理
26 | │
27 | │ 📄 文件路径: cc.py
28 | │ 📍 行号: 6
29 | │
30 | │ 🔑 密钥类型: 🤖 OpenAI API Key
31 | │ 🔐 密钥内容: sk-K*******************************************4fzK
32 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
33 | │
34 | │ 💻 代码片段:
35 | │ openai.api_key = os.environ.get("sk-KKaB5nstnno8fLUa3B1ST3BlbkFJN0ic4Y3AiAgw4kDS
36 | │
37 | │ 🕐 发现时间: 2025-10-16 09:10:04
38 | │
39 | └──────────────────────────────────────────────────────────────────────────
40 |
41 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
42 | │
43 | │ 🔴 风险等级: 高危 - 立即处理
44 | │
45 | │ 📄 文件路径: logics.py
46 | │ 📍 行号: 87
47 | │
48 | │ 🔑 密钥类型: 🤖 OpenAI API Key
49 | │ 🔐 密钥内容: sk-K*******************************************4fzK
50 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
51 | │
52 | │ 💻 代码片段:
53 | │ openai.api_key = "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"
54 | │
55 | │ 🕐 发现时间: 2025-10-16 09:10:04
56 | │
57 | └──────────────────────────────────────────────────────────────────────────
58 |
59 |
60 |
61 | ╭──────────────────────────────────────────────────────────────────────────────╮
62 | │ 📦 仓库: Niranjan5168/EnginSync-Placement-preparation-for-Engineering-Students │
63 | │ 🔗 地址: https://github.com/Niranjan5168/EnginSync-Placement-preparation-for-Engineering-Students│
64 | │ 🟡 中危 发现 2 个问题 │
65 | ╰──────────────────────────────────────────────────────────────────────────────╯
66 |
67 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
68 | │
69 | │ 🟡 风险等级: 中危 - 尽快处理
70 | │
71 | │ 📄 文件路径: .env
72 | │ 📍 行号: 1
73 | │
74 | │ 🔑 密钥类型: 🔍 Google AI API Key (Gemini)
75 | │ 🔐 密钥内容: AIza*******************************CiJY
76 | │ 🎯 匹配规则: 📌 Google AI/Gemini API Key 格式 (AIza...)
77 | │
78 | │ 💻 代码片段:
79 | │ YOUTUBE_API_KEY = AIzaSyCIfWqAkkAMtY3dBhSD4bUxK2dh2JkCiJY
80 | │
81 | │ 🕐 发现时间: 2025-10-16 09:16:08
82 | │
83 | └──────────────────────────────────────────────────────────────────────────
84 |
85 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
86 | │
87 | │ 🟡 风险等级: 中危 - 尽快处理
88 | │
89 | │ 📄 文件路径: .env
90 | │ 📍 行号: 4
91 | │
92 | │ 🔑 密钥类型: 🔍 Google AI API Key (Gemini)
93 | │ 🔐 密钥内容: AIza*******************************UHgY
94 | │ 🎯 匹配规则: 📌 Google AI/Gemini API Key 格式 (AIza...)
95 | │
96 | │ 💻 代码片段:
97 | │ GEMINI_API_KEY = AIzaSyDiKUzU3WChYPrjs1nQgX5HL-t5l-8UHgY
98 | │
99 | │ 🕐 发现时间: 2025-10-16 09:16:08
100 | │
101 | └──────────────────────────────────────────────────────────────────────────
102 |
103 |
104 |
105 | ╭──────────────────────────────────────────────────────────────────────────────╮
106 | │ 📦 仓库: wsy258-strar/DocGPT │
107 | │ 🔗 地址: https://github.com/wsy258-strar/DocGPT │
108 | │ 🟡 中危 发现 1 个问题 │
109 | ╰──────────────────────────────────────────────────────────────────────────────╯
110 |
111 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
112 | │
113 | │ 🟡 风险等级: 中危 - 尽快处理
114 | │
115 | │ 📄 文件路径: Dockerfile
116 | │ 📍 行号: 100
117 | │
118 | │ 🔑 密钥类型: 🤖 OpenAI API Key
119 | │ 🔐 密钥内容: sk-I*******************************************v1o3
120 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
121 | │
122 | │ 💻 代码片段:
123 | │ # ENV OPENAI_API_KEY = "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"
124 | │
125 | │ 🕐 发现时间: 2025-10-16 09:41:46
126 | │
127 | └──────────────────────────────────────────────────────────────────────────
128 |
129 |
130 |
131 | ╔══════════════════════════════════════════════════════════════════════════════╗
132 | ║ ║
133 | ║ 📊 统计信息与分析 ║
134 | ║ ║
135 | ╚══════════════════════════════════════════════════════════════════════════════╝
136 |
137 | ┌─ 风险等级分布
138 | │
139 | │ 🔴 高危问题: 2 个 ( 40.0%) ████████
140 | │ 🟡 中危问题: 3 个 ( 60.0%) ████████████
141 | │ 🟢 低危问题: 0 个 ( 0.0%)
142 | │
143 | │ 📊 总计: 5 个潜在问题
144 | └──────────────────────────────────────────────────────────────────────────────
145 |
146 | ┌─ 影响范围
147 | │
148 | │ 📦 涉及仓库: 3 个
149 | │ 📄 涉及文件: 4 个
150 | │
151 | └──────────────────────────────────────────────────────────────────────────────
152 |
153 | ┌─ 密钥类型分布
154 | │
155 | │ 🤖 OpenAI API Key: 3 个
156 | │ 🔍 Google AI API Key (Gemini): 2 个
157 | │
158 | └──────────────────────────────────────────────────────────────────────────────
159 |
160 | ╔══════════════════════════════════════════════════════════════════════════════╗
161 | ║ 🛡️ 安全建议 ║
162 | ╚══════════════════════════════════════════════════════════════════════════════╝
163 |
164 | ⚠️ 立即行动(针对高危问题):
165 | 1. 🚨 立即撤销/轮换所有泄露的 API 密钥
166 | 2. 🔍 检查 API 使用日志,确认是否被滥用
167 | 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)
168 | 4. 📧 通知相关团队成员
169 |
170 | 🔒 长期防护措施:
171 | 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)
172 | 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件
173 | 3. 🪝 配置 pre-commit hooks 防止敏感信息提交
174 | 4. 🔄 定期轮换 API 密钥
175 | 5. 👥 对团队进行安全培训
176 | 6. 📊 定期运行此扫描工具进行审查
177 |
178 | 📚 参考资源:
179 | • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning
180 | • Git 历史清理: https://github.com/newren/git-filter-repo
181 | • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
182 |
183 | ╔══════════════════════════════════════════════════════════════════════════════╗
184 | ║ ║
185 | ║ ✅ 报告生成完成 - 请及时处理发现的问题 ║
186 | ║ ║
187 | ║ 生成时间: 2025年10月16日 09:42:44 ║
188 | ║ 报告位置: ./scan_reports/scan_report_20251016_094244.txt ║
189 | ║ ║
190 | ╚══════════════════════════════════════════════════════════════════════════════╝
191 |
--------------------------------------------------------------------------------
/scan_reports/scan_report_20251017_032844.txt:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════════════════════════════════════╗
2 | ║ ║
3 | ║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
4 | ║ ║
5 | ╚══════════════════════════════════════════════════════════════════════════════╝
6 |
7 | 📋 扫描信息
8 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9 | 🎯 扫描类型: 🤖 自动搜索 AI 项目
10 | ⏱️ 开始时间: 2025-10-17 03:00:34
11 | ⏱️ 结束时间: 2025-10-17 03:28:44
12 | ⏳ 扫描耗时: 28分10秒
13 | 🔴 发现问题数: 5 个 (🔴 4 高危, 🟡 1 中危)
14 | 📦 涉及仓库数: 3 个
15 |
16 |
17 | ╭──────────────────────────────────────────────────────────────────────────────╮
18 | │ 📦 仓库: Sasmitha6821/Kalix-Chatbot │
19 | │ 🔗 地址: https://github.com/Sasmitha6821/Kalix-Chatbot │
20 | │ 🔴 高危 发现 3 个问题 │
21 | ╰──────────────────────────────────────────────────────────────────────────────╯
22 |
23 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
24 | │
25 | │ 🔴 风险等级: 高危 - 立即处理
26 | │
27 | │ 📄 文件路径: Applied Project 2/ChatBot/API.py
28 | │ 📍 行号: 4
29 | │
30 | │ 🔑 密钥类型: 🤖 OpenAI API Key
31 | │ 🔐 密钥内容: sk-a*******************************************MfKE
32 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
33 | │
34 | │ 💻 代码片段:
35 | │ openai.api_key = "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"
36 | │
37 | │ 🕐 发现时间: 2025-10-17 03:02:57
38 | │
39 | └──────────────────────────────────────────────────────────────────────────
40 |
41 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
42 | │
43 | │ 🔴 风险等级: 高危 - 立即处理
44 | │
45 | │ 📄 文件路径: Applied Project 2/ChatBot/Custom.py
46 | │ 📍 行号: 4
47 | │
48 | │ 🔑 密钥类型: 🤖 OpenAI API Key
49 | │ 🔐 密钥内容: sk-a*******************************************MfKE
50 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
51 | │
52 | │ 💻 代码片段:
53 | │ openai.api_key = "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"
54 | │
55 | │ 🕐 发现时间: 2025-10-17 03:02:57
56 | │
57 | └──────────────────────────────────────────────────────────────────────────
58 |
59 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
60 | │
61 | │ 🔴 风险等级: 高危 - 立即处理
62 | │
63 | │ 📄 文件路径: Applied Project 2/ChatBot/KalixP2.py
64 | │ 📍 行号: 3
65 | │
66 | │ 🔑 密钥类型: 🤖 OpenAI API Key
67 | │ 🔐 密钥内容: sk-a*******************************************MfKE
68 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
69 | │
70 | │ 💻 代码片段:
71 | │ openai.api_key = "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"
72 | │
73 | │ 🕐 发现时间: 2025-10-17 03:02:57
74 | │
75 | └──────────────────────────────────────────────────────────────────────────
76 |
77 |
78 |
79 | ╭──────────────────────────────────────────────────────────────────────────────╮
80 | │ 📦 仓库: BitCodeHub/YappyAI │
81 | │ 🔗 地址: https://github.com/BitCodeHub/YappyAI │
82 | │ 🟡 中危 发现 1 个问题 │
83 | ╰──────────────────────────────────────────────────────────────────────────────╯
84 |
85 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
86 | │
87 | │ 🟡 风险等级: 中危 - 尽快处理
88 | │
89 | │ 📄 文件路径: app_complete_v3.py
90 | │ 📍 行号: 286
91 | │
92 | │ 🔑 密钥类型: 🔍 Google AI API Key (Gemini)
93 | │ 🔐 密钥内容: AIza*******************************N-2Y
94 | │ 🎯 匹配规则: 📌 Google AI/Gemini API Key 格式 (AIza...)
95 | │
96 | │ 💻 代码片段:
97 | │ "key": "AIzaSyB-UeDa8gvn7QiMgLRVe8PF3gFMea2N-2Y", # Free public key with limits
98 | │
99 | │ 🕐 发现时间: 2025-10-17 03:06:49
100 | │
101 | └──────────────────────────────────────────────────────────────────────────
102 |
103 |
104 |
105 | ╭──────────────────────────────────────────────────────────────────────────────╮
106 | │ 📦 仓库: mharish27/Hazard-Free-Navigation │
107 | │ 🔗 地址: https://github.com/mharish27/Hazard-Free-Navigation │
108 | │ 🔴 高危 发现 1 个问题 │
109 | ╰──────────────────────────────────────────────────────────────────────────────╯
110 |
111 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
112 | │
113 | │ 🔴 风险等级: 高危 - 立即处理
114 | │
115 | │ 📄 文件路径: app.py
116 | │ 📍 行号: 170
117 | │
118 | │ 🔑 密钥类型: 🤖 OpenAI API Key
119 | │ 🔐 密钥内容: sk-G*******************************************V5Nl
120 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
121 | │
122 | │ 💻 代码片段:
123 | │ openai.api_key = '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'
124 | │
125 | │ 🕐 发现时间: 2025-10-17 03:23:37
126 | │
127 | └──────────────────────────────────────────────────────────────────────────
128 |
129 |
130 |
131 | ╔══════════════════════════════════════════════════════════════════════════════╗
132 | ║ ║
133 | ║ 📊 统计信息与分析 ║
134 | ║ ║
135 | ╚══════════════════════════════════════════════════════════════════════════════╝
136 |
137 | ┌─ 风险等级分布
138 | │
139 | │ 🔴 高危问题: 4 个 ( 80.0%) ████████████████
140 | │ 🟡 中危问题: 1 个 ( 20.0%) ████
141 | │ 🟢 低危问题: 0 个 ( 0.0%)
142 | │
143 | │ 📊 总计: 5 个潜在问题
144 | └──────────────────────────────────────────────────────────────────────────────
145 |
146 | ┌─ 影响范围
147 | │
148 | │ 📦 涉及仓库: 3 个
149 | │ 📄 涉及文件: 5 个
150 | │
151 | └──────────────────────────────────────────────────────────────────────────────
152 |
153 | ┌─ 密钥类型分布
154 | │
155 | │ 🤖 OpenAI API Key: 4 个
156 | │ 🔍 Google AI API Key (Gemini): 1 个
157 | │
158 | └──────────────────────────────────────────────────────────────────────────────
159 |
160 | ╔══════════════════════════════════════════════════════════════════════════════╗
161 | ║ 🛡️ 安全建议 ║
162 | ╚══════════════════════════════════════════════════════════════════════════════╝
163 |
164 | ⚠️ 立即行动(针对高危问题):
165 | 1. 🚨 立即撤销/轮换所有泄露的 API 密钥
166 | 2. 🔍 检查 API 使用日志,确认是否被滥用
167 | 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)
168 | 4. 📧 通知相关团队成员
169 |
170 | 🔒 长期防护措施:
171 | 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)
172 | 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件
173 | 3. 🪝 配置 pre-commit hooks 防止敏感信息提交
174 | 4. 🔄 定期轮换 API 密钥
175 | 5. 👥 对团队进行安全培训
176 | 6. 📊 定期运行此扫描工具进行审查
177 |
178 | 📚 参考资源:
179 | • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning
180 | • Git 历史清理: https://github.com/newren/git-filter-repo
181 | • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
182 |
183 | ╔══════════════════════════════════════════════════════════════════════════════╗
184 | ║ ║
185 | ║ ✅ 报告生成完成 - 请及时处理发现的问题 ║
186 | ║ ║
187 | ║ 生成时间: 2025年10月17日 03:28:44 ║
188 | ║ 报告位置: ./scan_reports/scan_report_20251017_032844.txt ║
189 | ║ ║
190 | ╚══════════════════════════════════════════════════════════════════════════════╝
191 |
--------------------------------------------------------------------------------
/github_scanner.py:
--------------------------------------------------------------------------------
1 | """
2 | GitHub仓库扫描模块
3 | """
4 | import time
5 | import re
6 | from datetime import datetime
7 | from typing import List, Dict, Optional
8 | from github import Github, GithubException
9 | from config import GITHUB_TOKEN, AI_SEARCH_KEYWORDS, MAX_REPOS_PER_SEARCH, SEARCH_DELAY_SECONDS
10 |
11 |
12 | class GitHubScanner:
13 | """GitHub仓库扫描器"""
14 |
15 | def __init__(self, token: str = GITHUB_TOKEN):
16 | """
17 | 初始化GitHub扫描器
18 |
19 | Args:
20 | token: GitHub Personal Access Token
21 | """
22 | if not token:
23 | raise ValueError("GitHub Token is required. Please set GITHUB_TOKEN in .env file")
24 |
25 | # 配置超时和重试参数,避免长时间等待
26 | self.github = Github(
27 | token,
28 | timeout=30, # 设置30秒超时
29 | retry=None # 禁用自动重试,我们自己处理
30 | )
31 | self.rate_limit_remaining = None
32 | self.rate_limit_reset = None
33 |
34 | def get_rate_limit_info(self) -> Dict:
35 | """获取API速率限制信息"""
36 | rate_limit = self.github.get_rate_limit()
37 | core = rate_limit.core
38 |
39 | return {
40 | 'remaining': core.remaining,
41 | 'limit': core.limit,
42 | 'reset': core.reset
43 | }
44 |
45 | def wait_for_rate_limit(self):
46 | """等待速率限制重置"""
47 | info = self.get_rate_limit_info()
48 | if info['remaining'] < 10:
49 | # info['reset'] 是 datetime 对象,需要和 datetime.now() 比较
50 | wait_time = (info['reset'] - datetime.now()).total_seconds() + 10
51 | print(f"⚠️ API速率限制即将耗尽,等待 {wait_time:.0f} 秒...")
52 | time.sleep(max(0, wait_time))
53 |
54 | def get_user_repos(self, username: str) -> List[Dict]:
55 | """
56 | 获取指定用户的所有公开仓库
57 |
58 | Args:
59 | username: GitHub用户名
60 |
61 | Returns:
62 | 仓库信息列表
63 | """
64 | try:
65 | user = self.github.get_user(username)
66 | repos = []
67 |
68 | for repo in user.get_repos():
69 | if not repo.private:
70 | repos.append({
71 | 'name': repo.name,
72 | 'full_name': repo.full_name,
73 | 'url': repo.html_url,
74 | 'clone_url': repo.clone_url,
75 | 'description': repo.description,
76 | 'updated_at': repo.updated_at,
77 | })
78 |
79 | return repos
80 | except GithubException as e:
81 | print(f"❌ 获取用户仓库失败: {e}")
82 | return []
83 |
84 | def get_org_repos(self, org_name: str) -> List[Dict]:
85 | """
86 | 获取指定组织的所有公开仓库
87 |
88 | Args:
89 | org_name: GitHub组织名
90 |
91 | Returns:
92 | 仓库信息列表
93 | """
94 | try:
95 | org = self.github.get_organization(org_name)
96 | repos = []
97 |
98 | for repo in org.get_repos():
99 | if not repo.private:
100 | repos.append({
101 | 'name': repo.name,
102 | 'full_name': repo.full_name,
103 | 'url': repo.html_url,
104 | 'clone_url': repo.clone_url,
105 | 'description': repo.description,
106 | 'updated_at': repo.updated_at,
107 | })
108 |
109 | return repos
110 | except GithubException as e:
111 | print(f"❌ 获取组织仓库失败: {e}")
112 | return []
113 |
114 | def search_ai_repos(self, max_repos: int = MAX_REPOS_PER_SEARCH, skip_filter=None) -> List[Dict]:
115 | """
116 | 搜索AI相关的GitHub项目
117 |
118 | Args:
119 | max_repos: 最大返回仓库数量
120 | skip_filter: 可选的过滤函数,接受仓库全名,返回True表示跳过该仓库
121 |
122 | Returns:
123 | 仓库信息列表
124 | """
125 | all_repos = []
126 | seen_repos = set()
127 | skipped_count = 0
128 |
129 | for keyword in AI_SEARCH_KEYWORDS:
130 | try:
131 | print(f"🔍 搜索关键词: {keyword}")
132 | self.wait_for_rate_limit()
133 |
134 | # 搜索代码
135 | query = f'{keyword} in:file language:python'
136 | results = self.github.search_code(query, order='desc')
137 |
138 | # 从代码搜索结果中提取仓库
139 | for code in results:
140 | # 如果已经找到足够的仓库,停止搜索
141 | if len(all_repos) >= max_repos:
142 | break
143 |
144 | repo = code.repository
145 |
146 | # 跳过私有仓库和已经见过的仓库
147 | if repo.private or repo.full_name in seen_repos:
148 | continue
149 |
150 | seen_repos.add(repo.full_name)
151 |
152 | # 如果提供了过滤函数,检查是否应该跳过
153 | if skip_filter and skip_filter(repo.full_name):
154 | skipped_count += 1
155 | print(f" ⏭️ 跳过已扫描: {repo.full_name}")
156 | continue # 不计数,继续找下一个
157 |
158 | # 添加到结果列表
159 | all_repos.append({
160 | 'name': repo.name,
161 | 'full_name': repo.full_name,
162 | 'url': repo.html_url,
163 | 'clone_url': repo.clone_url,
164 | 'description': repo.description,
165 | 'updated_at': repo.updated_at,
166 | })
167 |
168 | # 延迟以避免触发速率限制
169 | time.sleep(SEARCH_DELAY_SECONDS)
170 |
171 | if len(all_repos) >= max_repos:
172 | print(f"✅ 已找到 {len(all_repos)} 个未扫描的仓库(跳过了 {skipped_count} 个已扫描的)")
173 | break
174 |
175 | except GithubException as e:
176 | print(f"⚠️ 搜索 '{keyword}' 时出错: {e}")
177 | continue
178 |
179 | if skipped_count > 0 and len(all_repos) < max_repos:
180 | print(f"ℹ️ 找到 {len(all_repos)} 个未扫描的仓库(跳过了 {skipped_count} 个已扫描的)")
181 |
182 | return all_repos
183 |
184 | def get_repo_files(self, repo_full_name: str, path: str = "") -> List[Dict]:
185 | """
186 | 获取仓库中的文件列表
187 |
188 | Args:
189 | repo_full_name: 仓库全名 (owner/repo)
190 | path: 文件路径
191 |
192 | Returns:
193 | 文件信息列表
194 | """
195 | try:
196 | repo = self.github.get_repo(repo_full_name)
197 | contents = repo.get_contents(path)
198 |
199 | files = []
200 | for content in contents:
201 | if content.type == "dir":
202 | # 递归获取子目录文件
203 | files.extend(self.get_repo_files(repo_full_name, content.path))
204 | else:
205 | files.append({
206 | 'path': content.path,
207 | 'name': content.name,
208 | 'download_url': content.download_url,
209 | 'sha': content.sha,
210 | })
211 |
212 | return files
213 | except GithubException as e:
214 | # 403 错误直接跳过,不等待
215 | if e.status == 403:
216 | print(f" ⏭️ 跳过: 无权访问 (403 Forbidden)")
217 | else:
218 | print(f"⚠️ 获取文件列表失败: {e}")
219 | return []
220 |
221 | def get_file_content(self, repo_full_name: str, file_path: str) -> Optional[str]:
222 | """
223 | 获取文件内容
224 |
225 | Args:
226 | repo_full_name: 仓库全名 (owner/repo)
227 | file_path: 文件路径
228 |
229 | Returns:
230 | 文件内容(文本)
231 | """
232 | try:
233 | repo = self.github.get_repo(repo_full_name)
234 | content = repo.get_contents(file_path)
235 |
236 | # 解码内容
237 | try:
238 | return content.decoded_content.decode('utf-8')
239 | except UnicodeDecodeError:
240 | # 如果是二进制文件,返回None
241 | return None
242 | except GithubException as e:
243 | # 403 错误直接跳过,不打印错误
244 | if e.status == 403:
245 | pass # 静默跳过
246 | return None
247 |
--------------------------------------------------------------------------------
/.github/workflows/manual-scan.yml:
--------------------------------------------------------------------------------
1 | name: AI API Key Scanner - Manual Scan
2 |
3 | # 仅手动触发
4 | on:
5 | workflow_dispatch:
6 | inputs:
7 | scan_type:
8 | description: '扫描类型'
9 | required: true
10 | type: choice
11 | options:
12 | - 'auto - 自动搜索AI项目'
13 | - 'user - 扫描指定用户'
14 | - 'org - 扫描指定组织'
15 | - 'repo - 扫描单个仓库'
16 |
17 | target:
18 | description: '扫描目标(用户名/组织名/仓库全名,auto模式时可留空)'
19 | required: false
20 | type: string
21 | default: ''
22 |
23 | max_repos:
24 | description: '最大扫描仓库数(仅auto模式有效)'
25 | required: false
26 | type: number
27 | default: 50
28 |
29 | create_issue:
30 | description: '发现问题时是否创建Issue'
31 | required: false
32 | type: boolean
33 | default: true
34 |
35 | # 添加权限设置
36 | permissions:
37 | contents: write # 允许提交代码
38 | issues: write # 允许创建 Issue
39 |
40 | jobs:
41 | manual-scan:
42 | runs-on: ubuntu-latest
43 |
44 | steps:
45 | - name: 📥 检出代码
46 | uses: actions/checkout@v4
47 |
48 | - name: 🐍 设置 Python 3.10
49 | uses: actions/setup-python@v5
50 | with:
51 | python-version: '3.10'
52 |
53 | - name: 📦 安装依赖
54 | run: |
55 | python -m pip install --upgrade pip
56 | pip install -r requirements.txt
57 |
58 | - name: ⚙️ 配置环境
59 | run: |
60 | echo "GITHUB_TOKEN=${{ secrets.GH_SCAN_TOKEN }}" > .env
61 | echo "OUTPUT_DIR=./scan_reports" >> .env
62 | mkdir -p scan_reports
63 |
64 | - name: 🔍 执行扫描 - Auto模式
65 | if: startsWith(github.event.inputs.scan_type, 'auto')
66 | run: |
67 | echo "执行自动扫描,最多扫描 ${{ github.event.inputs.max_repos }} 个仓库"
68 | python scan_github.py --auto --max-repos ${{ github.event.inputs.max_repos }}
69 |
70 | - name: 🔍 执行扫描 - User模式
71 | if: startsWith(github.event.inputs.scan_type, 'user')
72 | run: |
73 | echo "扫描用户: ${{ github.event.inputs.target }}"
74 | python scan_github.py --user "${{ github.event.inputs.target }}"
75 |
76 | - name: 🔍 执行扫描 - Org模式
77 | if: startsWith(github.event.inputs.scan_type, 'org')
78 | run: |
79 | echo "扫描组织: ${{ github.event.inputs.target }}"
80 | python scan_github.py --org "${{ github.event.inputs.target }}"
81 |
82 | - name: 🔍 执行扫描 - Repo模式
83 | if: startsWith(github.event.inputs.scan_type, 'repo')
84 | run: |
85 | echo "扫描仓库: ${{ github.event.inputs.target }}"
86 | python scan_github.py --repo "${{ github.event.inputs.target }}"
87 |
88 | - name: 📊 生成扫描摘要
89 | if: always()
90 | run: |
91 | echo "# 🔍 扫描完成" >> $GITHUB_STEP_SUMMARY
92 | echo "" >> $GITHUB_STEP_SUMMARY
93 | echo "**扫描类型**: ${{ github.event.inputs.scan_type }}" >> $GITHUB_STEP_SUMMARY
94 | echo "**扫描目标**: ${{ github.event.inputs.target }}" >> $GITHUB_STEP_SUMMARY
95 | echo "**执行时间**: $(date)" >> $GITHUB_STEP_SUMMARY
96 | echo "" >> $GITHUB_STEP_SUMMARY
97 |
98 | if [ -f scan_reports/*.txt ]; then
99 | echo "## 📄 扫描结果" >> $GITHUB_STEP_SUMMARY
100 | echo '```' >> $GITHUB_STEP_SUMMARY
101 | head -n 80 scan_reports/*.txt >> $GITHUB_STEP_SUMMARY
102 | echo '```' >> $GITHUB_STEP_SUMMARY
103 | fi
104 |
105 | - name: 📤 上传报告
106 | if: always()
107 | uses: actions/upload-artifact@v4
108 | with:
109 | name: manual-scan-report-${{ github.run_number }}
110 | path: scan_reports/
111 | retention-days: 90
112 | if-no-files-found: ignore
113 |
114 | - name: 💾 提交扫描历史和报告
115 | if: always()
116 | run: |
117 | git config user.name "gaocaipeng"
118 | git config user.email "2540225402@qq.com"
119 |
120 | # 添加扫描历史
121 | git add scan_history/
122 |
123 | # 添加扫描报告
124 | if [ -d "scan_reports" ] && [ "$(ls -A scan_reports 2>/dev/null)" ]; then
125 | git add scan_reports/
126 | echo "✅ 发现扫描报告,将一并提交"
127 | fi
128 |
129 | # 检查是否有更改
130 | if git diff --cached --quiet; then
131 | echo "没有新的扫描历史或报告"
132 | else
133 | # 统计信息
134 | HISTORY_CHANGED=$(git diff --cached --name-only | grep "scan_history/" | wc -l | tr -d ' ')
135 | REPORTS_CHANGED=$(git diff --cached --name-only | grep "scan_reports/" | wc -l | tr -d ' ')
136 |
137 | # 提交消息
138 | COMMIT_MSG="📝 更新扫描数据 [skip ci]"
139 | if [ "$HISTORY_CHANGED" -gt 0 ] && [ "$REPORTS_CHANGED" -gt 0 ]; then
140 | COMMIT_MSG="📝 更新扫描历史和报告 [skip ci]"
141 | elif [ "$REPORTS_CHANGED" -gt 0 ]; then
142 | COMMIT_MSG="📄 添加扫描报告 [skip ci]"
143 | fi
144 |
145 | git commit -m "$COMMIT_MSG"
146 |
147 | # 同步远程更改,避免并发推送冲突
148 | git pull --rebase origin main || {
149 | echo "⚠️ Rebase 冲突,尝试使用本地版本"
150 | git rebase --abort
151 | git pull --rebase --strategy-option=ours origin main
152 | }
153 |
154 | git push
155 | echo "✅ 已提交: 历史文件 $HISTORY_CHANGED 个, 报告文件 $REPORTS_CHANGED 个"
156 | fi
157 |
158 | - name: 🔔 分析结果
159 | id: analyze
160 | if: always()
161 | run: |
162 | if [ -f scan_reports/*.txt ]; then
163 | FINDINGS=$(grep "发现的问题总数:" scan_reports/*.txt | grep -oE '[0-9]+' || echo "0")
164 | echo "发现 $FINDINGS 个潜在问题"
165 | echo "findings_count=$FINDINGS" >> $GITHUB_OUTPUT
166 |
167 | if [ "$FINDINGS" -gt 0 ]; then
168 | echo "has_findings=true" >> $GITHUB_OUTPUT
169 | else
170 | echo "has_findings=false" >> $GITHUB_OUTPUT
171 | fi
172 | else
173 | echo "findings_count=0" >> $GITHUB_OUTPUT
174 | echo "has_findings=false" >> $GITHUB_OUTPUT
175 | fi
176 |
177 | - name: 📢 创建告警Issue
178 | if: |
179 | always() &&
180 | github.event.inputs.create_issue == 'true' &&
181 | steps.analyze.outputs.has_findings == 'true'
182 | uses: actions/github-script@v7
183 | env:
184 | FINDINGS_COUNT: ${{ steps.analyze.outputs.findings_count }}
185 | SCAN_TYPE: ${{ github.event.inputs.scan_type }}
186 | SCAN_TARGET: ${{ github.event.inputs.target }}
187 | with:
188 | script: |
189 | const fs = require('fs');
190 | const reportDir = 'scan_reports';
191 | const findingsCount = process.env.FINDINGS_COUNT;
192 | const scanType = process.env.SCAN_TYPE;
193 | const scanTarget = process.env.SCAN_TARGET;
194 |
195 | if (fs.existsSync(reportDir)) {
196 | const files = fs.readdirSync(reportDir);
197 | if (files.length > 0) {
198 | const reportPath = `${reportDir}/${files[0]}`;
199 | const content = fs.readFileSync(reportPath, 'utf8');
200 | const preview = content.split('\n').slice(0, 120).join('\n');
201 |
202 | const issueTitle = `🚨 [手动扫描] 发现 ${findingsCount} 个潜在密钥泄露`;
203 | const issueBody = [
204 | '# 🔍 手动扫描报告',
205 | '',
206 | '## 扫描信息',
207 | '',
208 | `- 扫描类型: ${scanType}`,
209 | `- 扫描目标: ${scanTarget}`,
210 | `- 执行时间: ${new Date().toLocaleString('zh-CN')}`,
211 | `- 发现问题数: ${findingsCount}`,
212 | `- Workflow Run: [#${context.runNumber}](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`,
213 | '',
214 | '## 📄 报告预览',
215 | '',
216 | '```',
217 | preview,
218 | '```',
219 | '',
220 | '## 🔗 完整报告',
221 | '',
222 | `请从 [Artifacts](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) 下载完整报告。`,
223 | '',
224 | '## ⚠️ 建议操作',
225 | '',
226 | '1. 立即检查报告中标记的高置信度问题',
227 | '2. 轮换所有泄露的 API 密钥',
228 | '3. 使用环境变量或密钥管理服务',
229 | '4. 更新 .gitignore 防止未来泄露',
230 | '',
231 | '---',
232 | '*此 Issue 由 GitHub Actions 自动创建*'
233 | ].join('\n');
234 |
235 | await github.rest.issues.create({
236 | owner: context.repo.owner,
237 | repo: context.repo.repo,
238 | title: issueTitle,
239 | body: issueBody,
240 | labels: ['security', 'manual-scan', 'needs-review']
241 | });
242 | }
243 | }
244 |
245 |
--------------------------------------------------------------------------------
/scanner.py:
--------------------------------------------------------------------------------
1 | """
2 | 主扫描器模块 - 整合所有功能
3 | """
4 | import time
5 | from datetime import datetime
6 | from typing import List, Dict, Optional
7 | from github_scanner import GitHubScanner
8 | from secret_detector import SecretDetector
9 | from report_generator import ReportGenerator
10 | from scan_history import ScanHistory
11 |
12 |
13 | class CloudScanner:
14 | """云上扫描器 - 主要扫描逻辑"""
15 |
16 | def __init__(self, github_token: str, skip_scanned: bool = True, timeout_minutes: int = 50):
17 | """
18 | 初始化扫描器
19 |
20 | Args:
21 | github_token: GitHub Personal Access Token
22 | skip_scanned: 是否跳过已扫描的仓库 (默认: True)
23 | timeout_minutes: 扫描超时时间(分钟),默认50分钟
24 | """
25 | self.github_scanner = GitHubScanner(github_token)
26 | self.secret_detector = SecretDetector()
27 | self.report_generator = ReportGenerator()
28 | self.scan_history = ScanHistory()
29 | self.skip_scanned = skip_scanned
30 | self.timeout_seconds = timeout_minutes * 60
31 | self.scan_start_time = None
32 |
33 | def _is_timeout(self) -> bool:
34 | """检查是否超时"""
35 | if self.scan_start_time is None:
36 | return False
37 | elapsed = time.time() - self.scan_start_time
38 | return elapsed >= self.timeout_seconds
39 |
40 | def _check_timeout(self, current_idx: int, total_repos: int) -> bool:
41 | """
42 | 检查是否超时,如果超时则打印信息并返回True
43 |
44 | Args:
45 | current_idx: 当前扫描的仓库索引
46 | total_repos: 总仓库数
47 |
48 | Returns:
49 | 是否超时
50 | """
51 | if self._is_timeout():
52 | elapsed_minutes = (time.time() - self.scan_start_time) / 60
53 | print(f"\n⏰ 扫描超时(已运行 {elapsed_minutes:.1f} 分钟)")
54 | print(f"✅ 已完成 {current_idx}/{total_repos} 个仓库的扫描")
55 | print(f"💾 已保存前面的扫描数据,剩余 {total_repos - current_idx} 个仓库将在下次扫描时处理")
56 | return True
57 | return False
58 |
59 | def scan_user(self, username: str) -> str:
60 | """
61 | 扫描指定用户的所有公开仓库
62 |
63 | Args:
64 | username: GitHub用户名
65 |
66 | Returns:
67 | 报告文件路径
68 | """
69 | print(f"🚀 开始扫描用户: {username}")
70 | scan_start_time = datetime.now()
71 | self.scan_start_time = time.time() # 开始计时
72 |
73 | # 获取用户的所有仓库
74 | repos = self.github_scanner.get_user_repos(username)
75 | print(f"📦 找到 {len(repos)} 个公开仓库")
76 |
77 | # 过滤已扫描的仓库
78 | repos_to_scan, skipped_count = self._filter_scanned_repos(repos)
79 | if skipped_count > 0:
80 | print(f"⏭️ 跳过 {skipped_count} 个已扫描的仓库")
81 | print(f"📦 需要扫描 {len(repos_to_scan)} 个新仓库")
82 |
83 | # 扫描所有仓库
84 | all_findings = []
85 | for idx, repo in enumerate(repos_to_scan, 1):
86 | # 检查超时
87 | if self._check_timeout(idx - 1, len(repos_to_scan)):
88 | break
89 |
90 | print(f"🔍 [{idx}/{len(repos_to_scan)}] 扫描仓库: {repo['full_name']}")
91 | findings = self._scan_repository(repo, scan_type=f"user:{username}")
92 | all_findings.extend(findings)
93 |
94 | # 生成报告
95 | print(f"\n📝 生成报告...")
96 | report_path = self.report_generator.generate_report(
97 | all_findings,
98 | scan_start_time,
99 | scan_type=f"user:{username}"
100 | )
101 |
102 | # 打印摘要
103 | summary = self.report_generator.generate_summary(report_path, len(all_findings))
104 | print(summary)
105 |
106 | return report_path
107 |
108 | def scan_organization(self, org_name: str) -> str:
109 | """
110 | 扫描指定组织的所有公开仓库
111 |
112 | Args:
113 | org_name: GitHub组织名
114 |
115 | Returns:
116 | 报告文件路径
117 | """
118 | print(f"🚀 开始扫描组织: {org_name}")
119 | scan_start_time = datetime.now()
120 | self.scan_start_time = time.time() # 开始计时
121 |
122 | # 获取组织的所有仓库
123 | repos = self.github_scanner.get_org_repos(org_name)
124 | print(f"📦 找到 {len(repos)} 个公开仓库")
125 |
126 | # 过滤已扫描的仓库
127 | repos_to_scan, skipped_count = self._filter_scanned_repos(repos)
128 | if skipped_count > 0:
129 | print(f"⏭️ 跳过 {skipped_count} 个已扫描的仓库")
130 | print(f"📦 需要扫描 {len(repos_to_scan)} 个新仓库")
131 |
132 | # 扫描所有仓库
133 | all_findings = []
134 | for idx, repo in enumerate(repos_to_scan, 1):
135 | # 检查超时
136 | if self._check_timeout(idx - 1, len(repos_to_scan)):
137 | break
138 |
139 | print(f"🔍 [{idx}/{len(repos_to_scan)}] 扫描仓库: {repo['full_name']}")
140 | findings = self._scan_repository(repo, scan_type=f"org:{org_name}")
141 | all_findings.extend(findings)
142 |
143 | # 生成报告
144 | print(f"\n📝 生成报告...")
145 | report_path = self.report_generator.generate_report(
146 | all_findings,
147 | scan_start_time,
148 | scan_type=f"org:{org_name}"
149 | )
150 |
151 | # 打印摘要
152 | summary = self.report_generator.generate_summary(report_path, len(all_findings))
153 | print(summary)
154 |
155 | return report_path
156 |
157 | def scan_ai_projects(self, max_repos: int = 50) -> str:
158 | """
159 | 自动搜索并扫描AI相关项目
160 |
161 | Args:
162 | max_repos: 最大扫描仓库数
163 |
164 | Returns:
165 | 报告文件路径
166 | """
167 | print(f"🚀 开始自动搜索 AI 相关项目")
168 | print(f"🎯 目标: 找到并扫描 {max_repos} 个未扫描的仓库")
169 | scan_start_time = datetime.now()
170 | self.scan_start_time = time.time() # 开始计时
171 |
172 | # 定义过滤函数:检查仓库是否已扫描
173 | def is_scanned(repo_full_name: str) -> bool:
174 | return self.scan_history.is_scanned(repo_full_name)
175 |
176 | # 搜索仓库,实时过滤已扫描的
177 | # 搜索过程会自动跳过已扫描的仓库,直到找到足够数量的新仓库
178 | repos_to_scan = self.github_scanner.search_ai_repos(
179 | max_repos=max_repos,
180 | skip_filter=is_scanned if self.skip_scanned else None
181 | )
182 |
183 | print(f"📦 找到 {len(repos_to_scan)} 个待扫描的仓库")
184 |
185 | # 扫描所有仓库
186 | all_findings = []
187 | for idx, repo in enumerate(repos_to_scan, 1):
188 | # 检查超时
189 | if self._check_timeout(idx - 1, len(repos_to_scan)):
190 | break
191 |
192 | print(f"🔍 [{idx}/{len(repos_to_scan)}] 扫描仓库: {repo['full_name']}")
193 | findings = self._scan_repository(repo, scan_type="auto:ai-projects")
194 | all_findings.extend(findings)
195 |
196 | # 生成报告
197 | print(f"\n📝 生成报告...")
198 | report_path = self.report_generator.generate_report(
199 | all_findings,
200 | scan_start_time,
201 | scan_type="auto:ai-projects"
202 | )
203 |
204 | # 打印摘要
205 | summary = self.report_generator.generate_summary(report_path, len(all_findings))
206 | print(summary)
207 |
208 | return report_path
209 |
210 | def scan_single_repo(self, repo_full_name: str) -> str:
211 | """
212 | 扫描单个仓库
213 |
214 | Args:
215 | repo_full_name: 仓库全名 (owner/repo)
216 |
217 | Returns:
218 | 报告文件路径
219 | """
220 | print(f"🚀 开始扫描仓库: {repo_full_name}")
221 | scan_start_time = datetime.now()
222 |
223 | # 构建仓库信息
224 | repo_info = {
225 | 'full_name': repo_full_name,
226 | 'url': f"https://github.com/{repo_full_name}",
227 | 'clone_url': f"https://github.com/{repo_full_name}.git",
228 | }
229 |
230 | # 扫描仓库
231 | findings = self._scan_repository(repo_info)
232 |
233 | # 生成报告
234 | print(f"\n📝 生成报告...")
235 | report_path = self.report_generator.generate_report(
236 | findings,
237 | scan_start_time,
238 | scan_type=f"single:{repo_full_name}"
239 | )
240 |
241 | # 打印摘要
242 | summary = self.report_generator.generate_summary(report_path, len(findings))
243 | print(summary)
244 |
245 | return report_path
246 |
247 | def _filter_scanned_repos(self, repos: List[Dict]) -> tuple:
248 | """
249 | 过滤已扫描的仓库
250 |
251 | Args:
252 | repos: 仓库列表
253 |
254 | Returns:
255 | (需要扫描的仓库列表, 跳过的仓库数量)
256 | """
257 | if not self.skip_scanned:
258 | return repos, 0
259 |
260 | repos_to_scan = []
261 | skipped_count = 0
262 |
263 | for repo in repos:
264 | repo_name = repo.get('full_name', '')
265 | if self.scan_history.is_scanned(repo_name):
266 | skipped_count += 1
267 | else:
268 | repos_to_scan.append(repo)
269 |
270 | return repos_to_scan, skipped_count
271 |
272 | def _scan_repository(self, repo: Dict, scan_type: str = "unknown") -> List[Dict]:
273 | """
274 | 扫描单个仓库
275 |
276 | Args:
277 | repo: 仓库信息字典
278 | scan_type: 扫描类型
279 |
280 | Returns:
281 | 发现的敏感信息列表
282 | """
283 | findings = []
284 | scan_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
285 | repo_name = repo.get('full_name', 'unknown')
286 |
287 | try:
288 | # 获取仓库文件列表
289 | files = self.github_scanner.get_repo_files(repo['full_name'])
290 |
291 | # 如果获取文件列表失败(例如403错误),直接返回
292 | if not files:
293 | # 记录到扫描历史,避免下次再扫
294 | self.scan_history.mark_as_scanned(repo_name, 0, f"{scan_type}:no-access")
295 | return findings
296 |
297 | # 扫描每个文件
298 | for file_info in files:
299 | # 检查是否应该扫描该文件
300 | if not self.secret_detector.should_scan_file(file_info['path']):
301 | continue
302 |
303 | # 获取文件内容
304 | content = self.github_scanner.get_file_content(
305 | repo['full_name'],
306 | file_info['path']
307 | )
308 |
309 | if content:
310 | # 检测敏感信息
311 | secrets = self.secret_detector.detect_secrets_in_text(
312 | content,
313 | file_info['path']
314 | )
315 |
316 | # 添加仓库信息
317 | for secret in secrets:
318 | secret['repo_url'] = repo.get('url', f"https://github.com/{repo_name}")
319 | secret['repo_name'] = repo['full_name']
320 | secret['scan_time'] = scan_time
321 | findings.append(secret)
322 |
323 | # 去重和过滤
324 | findings = self.secret_detector.deduplicate_findings(findings)
325 | findings = self.secret_detector.filter_high_confidence(findings)
326 |
327 | if findings:
328 | print(f" ⚠️ 发现 {len(findings)} 个潜在问题")
329 | else:
330 | print(f" ✅ 未发现明显问题")
331 |
332 | # 记录到扫描历史
333 | self.scan_history.mark_as_scanned(repo_name, len(findings), scan_type)
334 |
335 | except Exception as e:
336 | error_msg = str(e)
337 | # 403错误静默处理
338 | if "403" in error_msg or "Forbidden" in error_msg:
339 | print(f" ⏭️ 跳过: 无权访问")
340 | self.scan_history.mark_as_scanned(repo_name, 0, f"{scan_type}:forbidden")
341 | else:
342 | print(f" ❌ 扫描失败: {e}")
343 | # 即使扫描失败,也记录以避免反复尝试
344 | self.scan_history.mark_as_scanned(repo_name, 0, f"{scan_type}:failed")
345 |
346 | return findings
347 |
--------------------------------------------------------------------------------
/.github/workflows/scheduled-scan.yml:
--------------------------------------------------------------------------------
1 | name: AI API Key Scanner - Scheduled Scan
2 |
3 | # 多种定时触发选项
4 | on:
5 | schedule:
6 | # 每天 UTC 02:00 (北京时间 10:00)
7 | - cron: '0 2 * * *'
8 |
9 | # 也可以选择其他时间,取消注释即可:
10 | # - cron: '0 */6 * * *' # 每6小时一次
11 | # - cron: '0 0,12 * * *' # 每天 00:00 和 12:00
12 | # - cron: '0 2 * * 1' # 每周一 02:00
13 | # - cron: '0 2 1 * *' # 每月1号 02:00
14 |
15 | # 也支持手动触发
16 | workflow_dispatch:
17 |
18 | # 并发控制 - 同一时间只运行一个扫描任务
19 | concurrency:
20 | group: scheduled-scan
21 | cancel-in-progress: false
22 |
23 | # 添加权限设置
24 | permissions:
25 | contents: write # 允许提交代码
26 | issues: write # 允许创建和更新 Issue
27 |
28 | jobs:
29 | scheduled-scan:
30 | runs-on: ubuntu-latest
31 | timeout-minutes: 60 # 超时时间60分钟
32 |
33 | steps:
34 | - name: 📅 显示执行时间
35 | run: |
36 | echo "🕐 当前时间(UTC): $(date -u)"
37 | echo "🕐 当前时间(北京): $(TZ='Asia/Shanghai' date)"
38 |
39 | - name: 📥 检出代码
40 | uses: actions/checkout@v4
41 |
42 | - name: 🐍 设置 Python
43 | uses: actions/setup-python@v5
44 | with:
45 | python-version: '3.10'
46 |
47 | - name: 📦 安装依赖
48 | run: |
49 | python -m pip install --upgrade pip
50 | pip install -r requirements.txt
51 |
52 | - name: ⚙️ 配置环境
53 | env:
54 | GITHUB_SCAN_TOKEN: ${{ secrets.GH_SCAN_TOKEN }}
55 | run: |
56 | echo "GITHUB_TOKEN=${GITHUB_SCAN_TOKEN}" > .env
57 | echo "OUTPUT_DIR=./scan_reports" >> .env
58 | mkdir -p scan_reports
59 |
60 | - name: 🔍 执行定时扫描
61 | id: scan
62 | run: |
63 | echo "开始执行自动扫描任务..."
64 | python scan_github.py --auto --max-repos 50 2>&1 | tee scan.log
65 |
66 | # 记录扫描状态
67 | if [ $? -eq 0 ]; then
68 | echo "scan_status=success" >> $GITHUB_OUTPUT
69 | else
70 | echo "scan_status=failed" >> $GITHUB_OUTPUT
71 | fi
72 |
73 | - name: 📊 分析扫描结果
74 | id: analyze
75 | if: always()
76 | run: |
77 | # 检查是否有报告生成
78 | if [ -d "scan_reports" ] && [ "$(ls -A scan_reports)" ]; then
79 | REPORT_FILE=$(ls -t scan_reports/*.txt | head -1)
80 |
81 | # 提取发现的问题数
82 | TOTAL=$(grep "发现的问题总数:" "$REPORT_FILE" | grep -oE '[0-9]+' || echo "0")
83 | HIGH=$(grep "高置信度:" "$REPORT_FILE" | grep -oE '[0-9]+' || echo "0")
84 | MEDIUM=$(grep "中置信度:" "$REPORT_FILE" | grep -oE '[0-9]+' || echo "0")
85 |
86 | echo "total_findings=$TOTAL" >> $GITHUB_OUTPUT
87 | echo "high_confidence=$HIGH" >> $GITHUB_OUTPUT
88 | echo "medium_confidence=$MEDIUM" >> $GITHUB_OUTPUT
89 |
90 | echo "📊 扫描结果统计:"
91 | echo " - 总问题数: $TOTAL"
92 | echo " - 高置信度: $HIGH"
93 | echo " - 中置信度: $MEDIUM"
94 |
95 | # 判断是否需要告警
96 | if [ "$TOTAL" -gt 0 ]; then
97 | echo "needs_alert=true" >> $GITHUB_OUTPUT
98 | else
99 | echo "needs_alert=false" >> $GITHUB_OUTPUT
100 | fi
101 | else
102 | echo "⚠️ 未找到扫描报告"
103 | echo "total_findings=0" >> $GITHUB_OUTPUT
104 | echo "needs_alert=false" >> $GITHUB_OUTPUT
105 | fi
106 |
107 | - name: 📝 生成摘要
108 | if: always()
109 | run: |
110 | echo "# 🔍 定时扫描报告" >> $GITHUB_STEP_SUMMARY
111 | echo "" >> $GITHUB_STEP_SUMMARY
112 | echo "**执行时间**: $(date)" >> $GITHUB_STEP_SUMMARY
113 | echo "**扫描模式**: 自动搜索 AI 相关项目" >> $GITHUB_STEP_SUMMARY
114 | echo "**扫描状态**: ${{ steps.scan.outputs.scan_status }}" >> $GITHUB_STEP_SUMMARY
115 | echo "" >> $GITHUB_STEP_SUMMARY
116 |
117 | # 检查是否存在 txt 文件
118 | if ls scan_reports/*.txt 1> /dev/null 2>&1; then
119 | echo "## 📊 扫描统计" >> $GITHUB_STEP_SUMMARY
120 | echo "" >> $GITHUB_STEP_SUMMARY
121 | echo "- 🔴 高置信度问题: ${{ steps.analyze.outputs.high_confidence }}" >> $GITHUB_STEP_SUMMARY
122 | echo "- 🟡 中置信度问题: ${{ steps.analyze.outputs.medium_confidence }}" >> $GITHUB_STEP_SUMMARY
123 | echo "- 📦 总问题数: ${{ steps.analyze.outputs.total_findings }}" >> $GITHUB_STEP_SUMMARY
124 | echo "" >> $GITHUB_STEP_SUMMARY
125 |
126 | echo "## 📄 报告预览" >> $GITHUB_STEP_SUMMARY
127 | echo '```' >> $GITHUB_STEP_SUMMARY
128 | head -n 100 scan_reports/*.txt >> $GITHUB_STEP_SUMMARY
129 | echo '```' >> $GITHUB_STEP_SUMMARY
130 | fi
131 |
132 | - name: 📤 上传报告
133 | if: always()
134 | uses: actions/upload-artifact@v4
135 | with:
136 | name: scheduled-scan-${{ github.run_number }}
137 | path: |
138 | scan_reports/
139 | scan.log
140 | retention-days: 90
141 | if-no-files-found: ignore
142 |
143 | - name: 💾 提交扫描历史和报告
144 | if: always()
145 | run: |
146 | git config user.name "gaocaipeng"
147 | git config user.email "2540225402@qq.com"
148 |
149 | # 添加扫描历史
150 | git add scan_history/
151 |
152 | # 添加扫描报告
153 | if [ -d "scan_reports" ] && [ "$(ls -A scan_reports 2>/dev/null)" ]; then
154 | git add scan_reports/
155 | echo "✅ 发现扫描报告,将一并提交"
156 | fi
157 |
158 | # 检查是否有更改
159 | if git diff --cached --quiet; then
160 | echo "没有新的扫描历史或报告"
161 | else
162 | # 统计信息
163 | HISTORY_CHANGED=$(git diff --cached --name-only | grep "scan_history/" | wc -l | tr -d ' ')
164 | REPORTS_CHANGED=$(git diff --cached --name-only | grep "scan_reports/" | wc -l | tr -d ' ')
165 |
166 | # 提交消息
167 | COMMIT_MSG="📝 更新扫描数据 [skip ci]"
168 | if [ "$HISTORY_CHANGED" -gt 0 ] && [ "$REPORTS_CHANGED" -gt 0 ]; then
169 | COMMIT_MSG="📝 更新扫描历史和报告 [skip ci]"
170 | elif [ "$REPORTS_CHANGED" -gt 0 ]; then
171 | COMMIT_MSG="📄 添加扫描报告 [skip ci]"
172 | fi
173 |
174 | git commit -m "$COMMIT_MSG"
175 |
176 | # 同步远程更改,避免并发推送冲突
177 | git pull --rebase origin main || {
178 | echo "⚠️ Rebase 冲突,尝试使用本地版本"
179 | git rebase --abort
180 | git pull --rebase --strategy-option=ours origin main
181 | }
182 |
183 | git push
184 | echo "✅ 已提交: 历史文件 $HISTORY_CHANGED 个, 报告文件 $REPORTS_CHANGED 个"
185 | fi
186 |
187 | - name: 🚨 创建告警Issue(发现问题时)
188 | if: steps.analyze.outputs.needs_alert == 'true'
189 | uses: actions/github-script@v7
190 | env:
191 | TOTAL_FINDINGS: ${{ steps.analyze.outputs.total_findings }}
192 | HIGH_CONFIDENCE: ${{ steps.analyze.outputs.high_confidence }}
193 | MEDIUM_CONFIDENCE: ${{ steps.analyze.outputs.medium_confidence }}
194 | with:
195 | script: |
196 | const fs = require('fs');
197 | const reportFiles = fs.readdirSync('scan_reports');
198 |
199 | if (reportFiles.length > 0) {
200 | const reportPath = `scan_reports/${reportFiles[0]}`;
201 | const content = fs.readFileSync(reportPath, 'utf8');
202 | const preview = content.split('\n').slice(0, 150).join('\n');
203 |
204 | const total = process.env.TOTAL_FINDINGS;
205 | const high = process.env.HIGH_CONFIDENCE;
206 | const medium = process.env.MEDIUM_CONFIDENCE;
207 |
208 | // 检查是否已存在今天的issue
209 | const today = new Date().toISOString().split('T')[0];
210 | const existingIssues = await github.rest.issues.listForRepo({
211 | owner: context.repo.owner,
212 | repo: context.repo.repo,
213 | labels: 'auto-scan,security',
214 | state: 'open'
215 | });
216 |
217 | const todayIssue = existingIssues.data.find(issue =>
218 | issue.title.includes(today)
219 | );
220 |
221 | if (todayIssue) {
222 | // 更新现有issue
223 | const updateBody = [
224 | '## 🔄 更新:新的扫描发现',
225 | '',
226 | `扫描时间: ${new Date().toLocaleString('zh-CN')}`,
227 | `运行: [#${context.runNumber}](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`,
228 | '',
229 | '### 📊 本次扫描统计',
230 | `- 🔴 高置信度: ${high}`,
231 | `- 🟡 中置信度: ${medium}`,
232 | `- 📦 总计: ${total}`,
233 | '',
234 | '完整报告请查看 Artifacts。'
235 | ].join('\n');
236 |
237 | await github.rest.issues.createComment({
238 | owner: context.repo.owner,
239 | repo: context.repo.repo,
240 | issue_number: todayIssue.number,
241 | body: updateBody
242 | });
243 | } else {
244 | // 创建新issue
245 | const highWarning = high > 0 ? '⚠️ 需立即处理' : '';
246 | const mediumWarning = medium > 0 ? '⚠️ 建议审查' : '';
247 |
248 | const issueTitle = `🚨 [${today}] 安全扫描发现 ${total} 个潜在密钥泄露`;
249 | const issueBody = [
250 | '# 🔍 自动扫描告警',
251 | '',
252 | '## 📋 扫描信息',
253 | '',
254 | `- 扫描日期: ${today}`,
255 | `- 执行时间: ${new Date().toLocaleString('zh-CN')}`,
256 | `- Workflow: [Run #${context.runNumber}](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`,
257 | '',
258 | '## 📊 发现问题统计',
259 | '',
260 | `- 🔴 高置信度: ${high} 个 ${highWarning}`,
261 | `- 🟡 中置信度: ${medium} 个 ${mediumWarning}`,
262 | `- 📦 总计: ${total} 个`,
263 | '',
264 | '## 📄 报告预览',
265 | '',
266 | '```',
267 | preview,
268 | '```',
269 | '',
270 | '## 🔗 完整报告',
271 | '',
272 | `请从 [Artifacts](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) 下载完整的扫描报告。`,
273 | '',
274 | '## ⚠️ 建议的处理步骤',
275 | '',
276 | '1. 立即审查:检查所有高置信度的发现',
277 | '2. 轮换密钥:对于确认泄露的密钥,立即在服务商处轮换',
278 | '3. 清理历史:使用 git-filter-repo 或 BFG 清理 Git 历史',
279 | '4. 防范措施:',
280 | ' - 使用环境变量存储敏感信息',
281 | ' - 更新 .gitignore 文件',
282 | ' - 配置 pre-commit hooks',
283 | ' - 启用 GitHub Secret Scanning',
284 | '',
285 | '## 📅 后续扫描',
286 | '',
287 | '下次自动扫描将在明天同一时间执行。',
288 | '',
289 | '---',
290 | `*🤖 此 Issue 由 GitHub Actions 自动创建 - [查看工作流](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/.github/workflows/scheduled-scan.yml)*`
291 | ].join('\n');
292 |
293 | await github.rest.issues.create({
294 | owner: context.repo.owner,
295 | repo: context.repo.repo,
296 | title: issueTitle,
297 | body: issueBody,
298 | labels: ['security', 'auto-scan', 'needs-review'],
299 | assignees: []
300 | });
301 | }
302 | }
303 |
304 | - name: ✅ 扫描完成
305 | if: always()
306 | run: |
307 | if [ "${{ steps.analyze.outputs.total_findings }}" -gt 0 ]; then
308 | echo "⚠️ 扫描完成,发现 ${{ steps.analyze.outputs.total_findings }} 个潜在问题"
309 | echo "请查看生成的 Issue 和 Artifacts 中的详细报告"
310 | else
311 | echo "✅ 扫描完成,未发现明显问题"
312 | fi
313 |
--------------------------------------------------------------------------------
/scan_reports/scan_report_20251016_044713.txt:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════════════════════════════════════╗
2 | ║ ║
3 | ║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
4 | ║ ║
5 | ╚══════════════════════════════════════════════════════════════════════════════╝
6 |
7 | 📋 扫描信息
8 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9 | 🎯 扫描类型: 🤖 自动搜索 AI 项目
10 | ⏱️ 开始时间: 2025-10-16 03:01:52
11 | ⏱️ 结束时间: 2025-10-16 04:47:13
12 | ⏳ 扫描耗时: 105分21秒
13 | 🔴 发现问题数: 11 个 (🔴 9 高危, 🟡 2 中危)
14 | 📦 涉及仓库数: 6 个
15 |
16 |
17 | ╭──────────────────────────────────────────────────────────────────────────────╮
18 | │ 📦 仓库: jayalath-jknr/pdf-streamlit │
19 | │ 🔗 地址: https://github.com/jayalath-jknr/pdf-streamlit │
20 | │ 🔴 高危 发现 3 个问题 │
21 | ╰──────────────────────────────────────────────────────────────────────────────╯
22 |
23 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
24 | │
25 | │ 🔴 风险等级: 高危 - 立即处理
26 | │
27 | │ 📄 文件路径: ap.py
28 | │ 📍 行号: 25
29 | │
30 | │ 🔑 密钥类型: 🤖 OpenAI API Key
31 | │ 🔐 密钥内容: sk-w*******************************************4gDS
32 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
33 | │
34 | │ 💻 代码片段:
35 | │ OPENAI_API_KEY = "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"
36 | │
37 | │ 🕐 发现时间: 2025-10-16 03:02:16
38 | │
39 | └──────────────────────────────────────────────────────────────────────────
40 |
41 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
42 | │
43 | │ 🔴 风险等级: 高危 - 立即处理
44 | │
45 | │ 📄 文件路径: app.py
46 | │ 📍 行号: 9
47 | │
48 | │ 🔑 密钥类型: 🤖 OpenAI API Key
49 | │ 🔐 密钥内容: sk-F*******************************************n0CX
50 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
51 | │
52 | │ 💻 代码片段:
53 | │ OPENAI_API_KEY = "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"
54 | │
55 | │ 🕐 发现时间: 2025-10-16 03:02:16
56 | │
57 | └──────────────────────────────────────────────────────────────────────────
58 |
59 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
60 | │
61 | │ 🔴 风险等级: 高危 - 立即处理
62 | │
63 | │ 📄 文件路径: appold2.py
64 | │ 📍 行号: 54
65 | │
66 | │ 🔑 密钥类型: 🤖 OpenAI API Key
67 | │ 🔐 密钥内容: sk-F*******************************************n0CX
68 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
69 | │
70 | │ 💻 代码片段:
71 | │ embeddings = OpenAIEmbeddings(openai_api_key="sk-F7s3C15MRa7UiL1r1nAFT3BlbkFJIim
72 | │
73 | │ 🕐 发现时间: 2025-10-16 03:02:16
74 | │
75 | └──────────────────────────────────────────────────────────────────────────
76 |
77 |
78 |
79 | ╭──────────────────────────────────────────────────────────────────────────────╮
80 | │ 📦 仓库: Ascensao/python_chatGPT_voice │
81 | │ 🔗 地址: https://github.com/Ascensao/python_chatGPT_voice │
82 | │ 🔴 高危 发现 2 个问题 │
83 | ╰──────────────────────────────────────────────────────────────────────────────╯
84 |
85 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
86 | │
87 | │ 🔴 风险等级: 高危 - 立即处理
88 | │
89 | │ 📄 文件路径: code_copy.txt
90 | │ 📍 行号: 7
91 | │
92 | │ 🔑 密钥类型: 🤖 OpenAI API Key
93 | │ 🔐 密钥内容: sk-C*******************************************D3DO
94 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
95 | │
96 | │ 💻 代码片段:
97 | │ openai.api_key = "sk-CDUDYs3rDIq1n4L9iS2AG3B2bkFJzWEvKLw8ys9ARv19D3DO"
98 | │
99 | │ 🕐 发现时间: 2025-10-16 03:06:00
100 | │
101 | └──────────────────────────────────────────────────────────────────────────
102 |
103 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
104 | │
105 | │ 🔴 风险等级: 高危 - 立即处理
106 | │
107 | │ 📄 文件路径: python_chatGPT_voice_assistant/python_chatGPT_voice_assistant.py
108 | │ 📍 行号: 7
109 | │
110 | │ 🔑 密钥类型: 🤖 OpenAI API Key
111 | │ 🔐 密钥内容: sk-C*******************************************D3DO
112 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
113 | │
114 | │ 💻 代码片段:
115 | │ openai.api_key = "sk-CDUDYs3rDIq1n4L9iS2AG3B2bkFJzWEvKLw8ys9ARv19D3DO"
116 | │
117 | │ 🕐 发现时间: 2025-10-16 03:06:00
118 | │
119 | └──────────────────────────────────────────────────────────────────────────
120 |
121 |
122 |
123 | ╭──────────────────────────────────────────────────────────────────────────────╮
124 | │ 📦 仓库: AdityaKanungo/OpenAI_Data_Reporting │
125 | │ 🔗 地址: https://github.com/AdityaKanungo/OpenAI_Data_Reporting │
126 | │ 🔴 高危 发现 1 个问题 │
127 | ╰──────────────────────────────────────────────────────────────────────────────╯
128 |
129 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
130 | │
131 | │ 🔴 风险等级: 高危 - 立即处理
132 | │
133 | │ 📄 文件路径: new_latest
134 | │ 📍 行号: 45
135 | │
136 | │ 🔑 密钥类型: 🤖 OpenAI API Key
137 | │ 🔐 密钥内容: sk-g*******************************************zLC0
138 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
139 | │
140 | │ 💻 代码片段:
141 | │ openai.api_key = "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"
142 | │
143 | │ 🕐 发现时间: 2025-10-16 03:39:31
144 | │
145 | └──────────────────────────────────────────────────────────────────────────
146 |
147 |
148 |
149 | ╭──────────────────────────────────────────────────────────────────────────────╮
150 | │ 📦 仓库: linpz/chat-gpt-1 │
151 | │ 🔗 地址: https://github.com/linpz/chat-gpt-1 │
152 | │ 🔴 高危 发现 1 个问题 │
153 | ╰──────────────────────────────────────────────────────────────────────────────╯
154 |
155 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
156 | │
157 | │ 🔴 风险等级: 高危 - 立即处理
158 | │
159 | │ 📄 文件路径: 11.py
160 | │ 📍 行号: 25
161 | │
162 | │ 🔑 密钥类型: 🤖 OpenAI API Key
163 | │ 🔐 密钥内容: sk-y*******************************************mR96
164 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
165 | │
166 | │ 💻 代码片段:
167 | │ os.environ["OPENAI_API_KEY"] ="sk-yxjhxT48HJqa30LBXpazT3BlbkFJWJB6OOE8GhTWJEOemR
168 | │
169 | │ 🕐 发现时间: 2025-10-16 03:42:08
170 | │
171 | └──────────────────────────────────────────────────────────────────────────
172 |
173 |
174 |
175 | ╭──────────────────────────────────────────────────────────────────────────────╮
176 | │ 📦 仓库: chanleeip/python_anywhere │
177 | │ 🔗 地址: https://github.com/chanleeip/python_anywhere │
178 | │ 🔴 高危 发现 2 个问题 │
179 | ╰──────────────────────────────────────────────────────────────────────────────╯
180 |
181 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
182 | │
183 | │ 🔴 风险等级: 高危 - 立即处理
184 | │
185 | │ 📄 文件路径: cc.py
186 | │ 📍 行号: 6
187 | │
188 | │ 🔑 密钥类型: 🤖 OpenAI API Key
189 | │ 🔐 密钥内容: sk-K*******************************************4fzK
190 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
191 | │
192 | │ 💻 代码片段:
193 | │ openai.api_key = os.environ.get("sk-KKaB5nstnno8fLUa3B1ST3BlbkFJN0ic4Y3AiAgw4kDS
194 | │
195 | │ 🕐 发现时间: 2025-10-16 03:42:44
196 | │
197 | └──────────────────────────────────────────────────────────────────────────
198 |
199 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
200 | │
201 | │ 🔴 风险等级: 高危 - 立即处理
202 | │
203 | │ 📄 文件路径: logics.py
204 | │ 📍 行号: 87
205 | │
206 | │ 🔑 密钥类型: 🤖 OpenAI API Key
207 | │ 🔐 密钥内容: sk-K*******************************************4fzK
208 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
209 | │
210 | │ 💻 代码片段:
211 | │ openai.api_key = "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"
212 | │
213 | │ 🕐 发现时间: 2025-10-16 03:42:44
214 | │
215 | └──────────────────────────────────────────────────────────────────────────
216 |
217 |
218 |
219 | ╭──────────────────────────────────────────────────────────────────────────────╮
220 | │ 📦 仓库: Niranjan5168/EnginSync-Placement-preparation-for-Engineering-Students │
221 | │ 🔗 地址: https://github.com/Niranjan5168/EnginSync-Placement-preparation-for-Engineering-Students│
222 | │ 🟡 中危 发现 2 个问题 │
223 | ╰──────────────────────────────────────────────────────────────────────────────╯
224 |
225 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
226 | │
227 | │ 🟡 风险等级: 中危 - 尽快处理
228 | │
229 | │ 📄 文件路径: .env
230 | │ 📍 行号: 1
231 | │
232 | │ 🔑 密钥类型: 🔍 Google AI API Key (Gemini)
233 | │ 🔐 密钥内容: AIza*******************************CiJY
234 | │ 🎯 匹配规则: 📌 Google AI/Gemini API Key 格式 (AIza...)
235 | │
236 | │ 💻 代码片段:
237 | │ YOUTUBE_API_KEY = AIzaSyCIfWqAkkAMtY3dBhSD4bUxK2dh2JkCiJY
238 | │
239 | │ 🕐 发现时间: 2025-10-16 03:48:32
240 | │
241 | └──────────────────────────────────────────────────────────────────────────
242 |
243 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
244 | │
245 | │ 🟡 风险等级: 中危 - 尽快处理
246 | │
247 | │ 📄 文件路径: .env
248 | │ 📍 行号: 4
249 | │
250 | │ 🔑 密钥类型: 🔍 Google AI API Key (Gemini)
251 | │ 🔐 密钥内容: AIza*******************************UHgY
252 | │ 🎯 匹配规则: 📌 Google AI/Gemini API Key 格式 (AIza...)
253 | │
254 | │ 💻 代码片段:
255 | │ GEMINI_API_KEY = AIzaSyDiKUzU3WChYPrjs1nQgX5HL-t5l-8UHgY
256 | │
257 | │ 🕐 发现时间: 2025-10-16 03:48:32
258 | │
259 | └──────────────────────────────────────────────────────────────────────────
260 |
261 |
262 |
263 | ╔══════════════════════════════════════════════════════════════════════════════╗
264 | ║ ║
265 | ║ 📊 统计信息与分析 ║
266 | ║ ║
267 | ╚══════════════════════════════════════════════════════════════════════════════╝
268 |
269 | ┌─ 风险等级分布
270 | │
271 | │ 🔴 高危问题: 9 个 ( 81.8%) ████████████████
272 | │ 🟡 中危问题: 2 个 ( 18.2%) ███
273 | │ 🟢 低危问题: 0 个 ( 0.0%)
274 | │
275 | │ 📊 总计: 11 个潜在问题
276 | └──────────────────────────────────────────────────────────────────────────────
277 |
278 | ┌─ 影响范围
279 | │
280 | │ 📦 涉及仓库: 6 个
281 | │ 📄 涉及文件: 10 个
282 | │
283 | └──────────────────────────────────────────────────────────────────────────────
284 |
285 | ┌─ 密钥类型分布
286 | │
287 | │ 🤖 OpenAI API Key: 9 个
288 | │ 🔍 Google AI API Key (Gemini): 2 个
289 | │
290 | └──────────────────────────────────────────────────────────────────────────────
291 |
292 | ╔══════════════════════════════════════════════════════════════════════════════╗
293 | ║ 🛡️ 安全建议 ║
294 | ╚══════════════════════════════════════════════════════════════════════════════╝
295 |
296 | ⚠️ 立即行动(针对高危问题):
297 | 1. 🚨 立即撤销/轮换所有泄露的 API 密钥
298 | 2. 🔍 检查 API 使用日志,确认是否被滥用
299 | 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)
300 | 4. 📧 通知相关团队成员
301 |
302 | 🔒 长期防护措施:
303 | 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)
304 | 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件
305 | 3. 🪝 配置 pre-commit hooks 防止敏感信息提交
306 | 4. 🔄 定期轮换 API 密钥
307 | 5. 👥 对团队进行安全培训
308 | 6. 📊 定期运行此扫描工具进行审查
309 |
310 | 📚 参考资源:
311 | • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning
312 | • Git 历史清理: https://github.com/newren/git-filter-repo
313 | • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
314 |
315 | ╔══════════════════════════════════════════════════════════════════════════════╗
316 | ║ ║
317 | ║ ✅ 报告生成完成 - 请及时处理发现的问题 ║
318 | ║ ║
319 | ║ 生成时间: 2025年10月16日 04:47:13 ║
320 | ║ 报告位置: ./scan_reports/scan_report_20251016_044713.txt ║
321 | ║ ║
322 | ╚══════════════════════════════════════════════════════════════════════════════╝
323 |
--------------------------------------------------------------------------------
/report_generator.py:
--------------------------------------------------------------------------------
1 | """
2 | 报告生成模块
3 | """
4 | import os
5 | from datetime import datetime
6 | from typing import List, Dict
7 | from config import OUTPUT_DIR
8 |
9 |
10 | class ReportGenerator:
11 | """扫描报告生成器"""
12 |
13 | def __init__(self, output_dir: str = OUTPUT_DIR):
14 | """
15 | 初始化报告生成器
16 |
17 | Args:
18 | output_dir: 输出目录
19 | """
20 | self.output_dir = output_dir
21 | self._ensure_output_dir()
22 |
23 | def _ensure_output_dir(self):
24 | """确保输出目录存在"""
25 | if not os.path.exists(self.output_dir):
26 | os.makedirs(self.output_dir)
27 |
28 | def generate_report(self,
29 | scan_results: List[Dict],
30 | scan_start_time: datetime,
31 | scan_type: str = "auto") -> str:
32 | """
33 | 生成扫描报告
34 |
35 | Args:
36 | scan_results: 扫描结果列表
37 | scan_start_time: 扫描开始时间
38 | scan_type: 扫描类型 (user/org/auto)
39 |
40 | Returns:
41 | 报告文件路径
42 | """
43 | report_time = datetime.now()
44 | timestamp = report_time.strftime("%Y%m%d_%H%M%S")
45 | filename = f"scan_report_{timestamp}.txt"
46 | filepath = os.path.join(self.output_dir, filename)
47 |
48 | with open(filepath, 'w', encoding='utf-8') as f:
49 | # 写入报告头
50 | f.write("╔" + "═" * 78 + "╗\n")
51 | f.write("║" + " " * 78 + "║\n")
52 | f.write("║" + " 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告".ljust(78) + "║\n")
53 | f.write("║" + " " * 78 + "║\n")
54 | f.write("╚" + "═" * 78 + "╝\n\n")
55 |
56 | # 扫描耗时
57 | duration = (report_time - scan_start_time).total_seconds()
58 | duration_str = f"{int(duration // 60)}分{int(duration % 60)}秒" if duration >= 60 else f"{int(duration)}秒"
59 |
60 | # 写入扫描信息
61 | f.write("📋 扫描信息\n")
62 | f.write("━" * 80 + "\n")
63 | f.write(f" 🎯 扫描类型: {self._format_scan_type(scan_type)}\n")
64 | f.write(f" ⏱️ 开始时间: {scan_start_time.strftime('%Y-%m-%d %H:%M:%S')}\n")
65 | f.write(f" ⏱️ 结束时间: {report_time.strftime('%Y-%m-%d %H:%M:%S')}\n")
66 | f.write(f" ⏳ 扫描耗时: {duration_str}\n")
67 |
68 | # 快速总览
69 | high_count = sum(1 for r in scan_results if r.get('confidence') == 'high')
70 | medium_count = sum(1 for r in scan_results if r.get('confidence') == 'medium')
71 | repos_count = len(set(r.get('repo_url') for r in scan_results)) if scan_results else 0
72 |
73 | status_emoji = "🔴" if high_count > 0 else "🟡" if medium_count > 0 else "✅"
74 | f.write(f" {status_emoji} 发现问题数: {len(scan_results)} 个")
75 | if len(scan_results) > 0:
76 | f.write(f" (🔴 {high_count} 高危, 🟡 {medium_count} 中危)")
77 | f.write("\n")
78 | f.write(f" 📦 涉及仓库数: {repos_count} 个\n")
79 | f.write("\n")
80 |
81 | # 如果没有发现问题
82 | if not scan_results:
83 | f.write("✅ 未发现敏感信息泄露!\n")
84 | f.write("\n扫描完成,一切正常。\n")
85 | else:
86 | # 按仓库分组
87 | results_by_repo = self._group_by_repo(scan_results)
88 |
89 | # 写入每个仓库的发现
90 | for repo_url, findings in results_by_repo.items():
91 | self._write_repo_findings(f, repo_url, findings)
92 |
93 | # 写入统计信息
94 | self._write_statistics(f, scan_results)
95 |
96 | # 写入报告尾
97 | f.write("\n╔" + "═" * 78 + "╗\n")
98 | f.write("║" + " " * 78 + "║\n")
99 | f.write("║" + " ✅ 报告生成完成 - 请及时处理发现的问题".ljust(78) + "║\n")
100 | f.write("║" + " " * 78 + "║\n")
101 | f.write("║" + f" 生成时间: {report_time.strftime('%Y年%m月%d日 %H:%M:%S')}".ljust(78) + "║\n")
102 | f.write("║" + f" 报告位置: {filepath}".ljust(78) + "║\n")
103 | f.write("║" + " " * 78 + "║\n")
104 | f.write("╚" + "═" * 78 + "╝\n")
105 |
106 | return filepath
107 |
108 | def _group_by_repo(self, scan_results: List[Dict]) -> Dict[str, List[Dict]]:
109 | """
110 | 按仓库分组扫描结果
111 |
112 | Args:
113 | scan_results: 扫描结果列表
114 |
115 | Returns:
116 | 按仓库分组的结果字典
117 | """
118 | grouped = {}
119 | for result in scan_results:
120 | repo_url = result.get('repo_url', 'Unknown')
121 | if repo_url not in grouped:
122 | grouped[repo_url] = []
123 | grouped[repo_url].append(result)
124 | return grouped
125 |
126 | def _format_scan_type(self, scan_type: str) -> str:
127 | """格式化扫描类型显示"""
128 | type_map = {
129 | 'auto:ai-projects': '🤖 自动搜索 AI 项目',
130 | 'user': '👤 指定用户扫描',
131 | 'org': '🏢 指定组织扫描',
132 | 'single': '📦 单个仓库扫描',
133 | }
134 | for key, value in type_map.items():
135 | if scan_type.startswith(key):
136 | return value
137 | return scan_type
138 |
139 | def _write_repo_findings(self, f, repo_url: str, findings: List[Dict]):
140 | """
141 | 写入单个仓库的发现
142 |
143 | Args:
144 | f: 文件对象
145 | repo_url: 仓库URL
146 | findings: 该仓库的发现列表
147 | """
148 | # 提取仓库名
149 | repo_name = repo_url.split('/')[-2:] if '/' in repo_url else [repo_url]
150 | repo_name = '/'.join(repo_name) if len(repo_name) == 2 else repo_url
151 |
152 | # 计算风险等级
153 | high_count = sum(1 for f in findings if f.get('confidence') == 'high')
154 | risk_level = "🔴 高危" if high_count > 0 else "🟡 中危"
155 |
156 | f.write("\n╭" + "─" * 78 + "╮\n")
157 | f.write(f"│ 📦 仓库: {repo_name}".ljust(80) + "│\n")
158 | f.write(f"│ 🔗 地址: {repo_url}".ljust(80) + "│\n")
159 | f.write(f"│ {risk_level} 发现 {len(findings)} 个问题".ljust(80) + "│\n")
160 | f.write("╰" + "─" * 78 + "╯\n\n")
161 |
162 | for idx, finding in enumerate(findings, 1):
163 | # 置信度标记
164 | confidence = finding.get('confidence', 'unknown')
165 | confidence_info = {
166 | 'high': ('🔴', '高危', '立即处理'),
167 | 'medium': ('🟡', '中危', '尽快处理'),
168 | 'low': ('🟢', '低危', '建议处理')
169 | }.get(confidence, ('⚪', '未知', '需要确认'))
170 |
171 | f.write(f" ┌─ 问题 #{idx} {'─' * 66}\n")
172 | f.write(f" │\n")
173 | f.write(f" │ {confidence_info[0]} 风险等级: {confidence_info[1]} - {confidence_info[2]}\n")
174 | f.write(f" │\n")
175 |
176 | # 文件信息
177 | file_path = finding.get('file_path', 'N/A')
178 | f.write(f" │ 📄 文件路径: {file_path}\n")
179 |
180 | # 行号
181 | if finding.get('line_number'):
182 | f.write(f" │ 📍 行号: {finding['line_number']}\n")
183 |
184 | # 发现的密钥
185 | secret = finding.get('secret', '')
186 | masked_secret = self._mask_secret(secret)
187 | secret_type = self._identify_secret_type(secret)
188 | f.write(f" │\n")
189 | f.write(f" │ 🔑 密钥类型: {secret_type}\n")
190 | f.write(f" │ 🔐 密钥内容: {masked_secret}\n")
191 |
192 | # 匹配来源(检测规则)
193 | if finding.get('pattern'):
194 | pattern_desc = self._explain_pattern(finding['pattern'])
195 | f.write(f" │ 🎯 匹配规则: {pattern_desc}\n")
196 |
197 | # 代码上下文
198 | if finding.get('line_content'):
199 | line_content = finding['line_content'].strip()[:80]
200 | f.write(f" │\n")
201 | f.write(f" │ 💻 代码片段:\n")
202 | f.write(f" │ {line_content}\n")
203 |
204 | # 扫描时间
205 | if finding.get('scan_time'):
206 | f.write(f" │\n")
207 | f.write(f" │ 🕐 发现时间: {finding['scan_time']}\n")
208 |
209 | f.write(f" │\n")
210 | f.write(f" └{'─' * 74}\n\n")
211 |
212 | f.write("\n")
213 |
214 | def _identify_secret_type(self, secret: str) -> str:
215 | """
216 | 识别密钥类型
217 |
218 | Args:
219 | secret: 密钥字符串
220 |
221 | Returns:
222 | 密钥类型描述
223 | """
224 | if secret.startswith('sk-proj-'):
225 | return '🤖 OpenAI API Key (Project)'
226 | elif secret.startswith('sk-ant-'):
227 | return '🤖 Anthropic API Key (Claude)'
228 | elif secret.startswith('sk-'):
229 | return '🤖 OpenAI API Key'
230 | elif secret.startswith('AIza'):
231 | return '🔍 Google AI API Key (Gemini)'
232 | elif 'openai' in secret.lower():
233 | return '🤖 OpenAI 相关密钥'
234 | elif 'anthropic' in secret.lower() or 'claude' in secret.lower():
235 | return '🤖 Anthropic 相关密钥'
236 | elif 'api_key' in secret.lower() or 'apikey' in secret.lower():
237 | return '🔑 通用 API Key'
238 | else:
239 | return '🔐 未知类型密钥'
240 |
241 | def _explain_pattern(self, pattern: str) -> str:
242 | """
243 | 将正则表达式模式转换为易读的描述
244 |
245 | Args:
246 | pattern: 正则表达式字符串
247 |
248 | Returns:
249 | 易读的模式描述
250 | """
251 | # 特定格式的密钥
252 | if 'sk-proj-' in pattern:
253 | return '📌 OpenAI Project API Key 格式 (sk-proj-...)'
254 | elif 'sk-ant-' in pattern:
255 | return '📌 Anthropic Claude API Key 格式 (sk-ant-...)'
256 | elif pattern == r'sk-[a-zA-Z0-9]{32,}':
257 | return '📌 OpenAI API Key 格式 (sk-...)'
258 | elif 'AIza' in pattern:
259 | return '📌 Google AI/Gemini API Key 格式 (AIza...)'
260 |
261 | # 环境变量模式
262 | elif 'OPENAI_API_KEY' in pattern:
263 | return '📌 OPENAI_API_KEY 环境变量赋值'
264 | elif 'AI_API_KEY' in pattern and 'OPENAI' not in pattern:
265 | return '📌 AI_API_KEY 环境变量赋值'
266 | elif 'ANTHROPIC_AUTH_TOKEN' in pattern:
267 | return '📌 ANTHROPIC_AUTH_TOKEN 环境变量赋值'
268 | elif 'ANTHROPIC_API_KEY' in pattern:
269 | return '📌 ANTHROPIC_API_KEY 环境变量赋值'
270 | elif 'CLAUDE_API_KEY' in pattern:
271 | return '📌 CLAUDE_API_KEY 环境变量赋值'
272 | elif 'CHAT_API_KEY' in pattern:
273 | return '📌 CHAT_API_KEY 环境变量赋值'
274 | elif 'GOOGLE_API_KEY' in pattern:
275 | return '📌 GOOGLE_API_KEY 环境变量赋值'
276 | elif 'GEMINI_API_KEY' in pattern:
277 | return '📌 GEMINI_API_KEY 环境变量赋值'
278 | elif 'AZURE_OPENAI' in pattern:
279 | return '📌 Azure OpenAI 环境变量赋值'
280 | elif 'HUGGINGFACE_API_KEY' in pattern:
281 | return '📌 HUGGINGFACE_API_KEY 环境变量赋值'
282 | elif 'HF_TOKEN' in pattern:
283 | return '📌 HF_TOKEN 环境变量赋值'
284 | elif 'COHERE_API_KEY' in pattern:
285 | return '📌 COHERE_API_KEY 环境变量赋值'
286 | elif 'API_KEY' in pattern and 'api_key' in pattern:
287 | return '📌 API_KEY/api_key 环境变量赋值'
288 |
289 | # camelCase/PascalCase 模式
290 | elif 'apiKey' in pattern and 'chat' not in pattern.lower() and 'openai' not in pattern.lower():
291 | return '📌 apiKey 对象属性/变量赋值'
292 | elif 'chatApiKey' in pattern:
293 | return '📌 chatApiKey 对象属性/变量赋值'
294 | elif 'openaiApiKey' in pattern or 'openAIKey' in pattern:
295 | return '📌 openaiApiKey/openAIKey 对象属性/变量赋值'
296 | elif 'anthropicApiKey' in pattern:
297 | return '📌 anthropicApiKey 对象属性/变量赋值'
298 |
299 | # 通用模式
300 | elif 'api_key' in pattern.lower():
301 | return '📌 通用 api_key 变量赋值'
302 |
303 | # 默认
304 | else:
305 | return f'📌 正则模式: {pattern[:50]}...' if len(pattern) > 50 else f'📌 正则模式: {pattern}'
306 |
307 | def _mask_secret(self, secret: str) -> str:
308 | """
309 | 部分隐藏密钥
310 |
311 | Args:
312 | secret: 原始密钥
313 |
314 | Returns:
315 | 隐藏后的密钥
316 | """
317 | if len(secret) <= 8:
318 | return "*" * len(secret)
319 |
320 | # 显示前4个和后4个字符
321 | return f"{secret[:4]}{'*' * (len(secret) - 8)}{secret[-4:]}"
322 |
323 | def _write_statistics(self, f, scan_results: List[Dict]):
324 | """
325 | 写入统计信息
326 |
327 | Args:
328 | f: 文件对象
329 | scan_results: 扫描结果列表
330 | """
331 | f.write("\n╔" + "═" * 78 + "╗\n")
332 | f.write("║" + " " * 78 + "║\n")
333 | f.write("║" + " 📊 统计信息与分析".ljust(78) + "║\n")
334 | f.write("║" + " " * 78 + "║\n")
335 | f.write("╚" + "═" * 78 + "╝\n\n")
336 |
337 | # 按置信度统计
338 | confidence_counts = {
339 | 'high': 0,
340 | 'medium': 0,
341 | 'low': 0
342 | }
343 |
344 | for result in scan_results:
345 | confidence = result.get('confidence', 'low')
346 | confidence_counts[confidence] = confidence_counts.get(confidence, 0) + 1
347 |
348 | f.write("┌─ 风险等级分布\n")
349 | f.write("│\n")
350 | total = len(scan_results)
351 | high_pct = (confidence_counts['high'] / total * 100) if total > 0 else 0
352 | medium_pct = (confidence_counts['medium'] / total * 100) if total > 0 else 0
353 | low_pct = (confidence_counts['low'] / total * 100) if total > 0 else 0
354 |
355 | f.write(f"│ 🔴 高危问题: {confidence_counts['high']:3d} 个 ({high_pct:5.1f}%)")
356 | f.write(f" {'█' * int(high_pct / 5)}\n")
357 | f.write(f"│ 🟡 中危问题: {confidence_counts['medium']:3d} 个 ({medium_pct:5.1f}%)")
358 | f.write(f" {'█' * int(medium_pct / 5)}\n")
359 | f.write(f"│ 🟢 低危问题: {confidence_counts['low']:3d} 个 ({low_pct:5.1f}%)")
360 | f.write(f" {'█' * int(low_pct / 5)}\n")
361 | f.write("│\n")
362 | f.write(f"│ 📊 总计: {total} 个潜在问题\n")
363 | f.write("└" + "─" * 78 + "\n\n")
364 |
365 | # 按仓库统计
366 | repos = set(r.get('repo_url') for r in scan_results)
367 | f.write("┌─ 影响范围\n")
368 | f.write("│\n")
369 | f.write(f"│ 📦 涉及仓库: {len(repos)} 个\n")
370 | f.write(f"│ 📄 涉及文件: {len(set(r.get('file_path') for r in scan_results))} 个\n")
371 | f.write("│\n")
372 | f.write("└" + "─" * 78 + "\n\n")
373 |
374 | # 按密钥类型统计
375 | secret_types = {}
376 | for result in scan_results:
377 | secret = result.get('secret', '')
378 | stype = self._identify_secret_type(secret)
379 | secret_types[stype] = secret_types.get(stype, 0) + 1
380 |
381 | if secret_types:
382 | f.write("┌─ 密钥类型分布\n")
383 | f.write("│\n")
384 | for stype, count in sorted(secret_types.items(), key=lambda x: x[1], reverse=True):
385 | f.write(f"│ {stype}: {count} 个\n")
386 | f.write("│\n")
387 | f.write("└" + "─" * 78 + "\n\n")
388 |
389 | # 安全建议
390 | f.write("╔" + "═" * 78 + "╗\n")
391 | f.write("║" + " 🛡️ 安全建议".ljust(78) + "║\n")
392 | f.write("╚" + "═" * 78 + "╝\n\n")
393 |
394 | f.write("⚠️ 立即行动(针对高危问题):\n")
395 | f.write(" 1. 🚨 立即撤销/轮换所有泄露的 API 密钥\n")
396 | f.write(" 2. 🔍 检查 API 使用日志,确认是否被滥用\n")
397 | f.write(" 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)\n")
398 | f.write(" 4. 📧 通知相关团队成员\n\n")
399 |
400 | f.write("🔒 长期防护措施:\n")
401 | f.write(" 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)\n")
402 | f.write(" 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件\n")
403 | f.write(" 3. 🪝 配置 pre-commit hooks 防止敏感信息提交\n")
404 | f.write(" 4. 🔄 定期轮换 API 密钥\n")
405 | f.write(" 5. 👥 对团队进行安全培训\n")
406 | f.write(" 6. 📊 定期运行此扫描工具进行审查\n\n")
407 |
408 | f.write("📚 参考资源:\n")
409 | f.write(" • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning\n")
410 | f.write(" • Git 历史清理: https://github.com/newren/git-filter-repo\n")
411 | f.write(" • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html\n")
412 |
413 | def generate_summary(self, report_path: str, total_findings: int) -> str:
414 | """
415 | 生成简要摘要
416 |
417 | Args:
418 | report_path: 报告文件路径
419 | total_findings: 发现的问题总数
420 |
421 | Returns:
422 | 摘要文本
423 | """
424 | if total_findings > 0:
425 | summary = f"""
426 | {'━' * 80}
427 | ✅ 扫描完成!
428 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
429 |
430 | 📄 报告已保存至: {report_path}
431 |
432 | ⚠️ 发现 {total_findings} 个潜在安全问题!
433 |
434 | 🔴 建议立即:
435 | 1. 查看详细报告
436 | 2. 撤销泄露的 API 密钥
437 | 3. 检查是否被滥用
438 | 4. 从 Git 历史中删除敏感信息
439 |
440 | {'━' * 80}
441 | """
442 | else:
443 | summary = f"""
444 | {'━' * 80}
445 | ✅ 扫描完成!
446 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
447 |
448 | 📄 报告已保存至: {report_path}
449 |
450 | 🎉 未发现明显的 API 密钥泄露!
451 |
452 | 💡 建议:
453 | • 继续保持良好的安全实践
454 | • 定期运行扫描检查
455 | • 对团队进行安全培训
456 |
457 | {'━' * 80}
458 | """
459 | return summary
460 |
--------------------------------------------------------------------------------
/scan_reports/scan_report_20251016_015525.txt:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════════════════════════════════════╗
2 | ║ ║
3 | ║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
4 | ║ ║
5 | ╚══════════════════════════════════════════════════════════════════════════════╝
6 |
7 | 📋 扫描信息
8 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9 | 🎯 扫描类型: 🤖 自动搜索 AI 项目
10 | ⏱️ 开始时间: 2025-10-16 01:31:34
11 | ⏱️ 结束时间: 2025-10-16 01:55:25
12 | ⏳ 扫描耗时: 23分50秒
13 | 🔴 发现问题数: 26 个 (🔴 25 高危, 🟡 1 中危)
14 | 📦 涉及仓库数: 6 个
15 |
16 |
17 | ╭──────────────────────────────────────────────────────────────────────────────╮
18 | │ 📦 仓库: Aliyansayz/CSVGPT │
19 | │ 🔗 地址: https://github.com/Aliyansayz/CSVGPT │
20 | │ 🔴 高危 发现 1 个问题 │
21 | ╰──────────────────────────────────────────────────────────────────────────────╯
22 |
23 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
24 | │
25 | │ 🔴 风险等级: 高危 - 立即处理
26 | │
27 | │ 📄 文件路径: .env
28 | │ 📍 行号: 1
29 | │
30 | │ 🔑 密钥类型: 🤖 OpenAI API Key
31 | │ 🔐 密钥内容: sk-J*******************************************LnA0
32 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
33 | │
34 | │ 💻 代码片段:
35 | │ OPENAI_API_KEY ="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"
36 | │
37 | │ 🕐 发现时间: 2025-10-16 01:36:07
38 | │
39 | └──────────────────────────────────────────────────────────────────────────
40 |
41 |
42 |
43 | ╭──────────────────────────────────────────────────────────────────────────────╮
44 | │ 📦 仓库: Elizabeth819/GPT4Video-cobra-auto │
45 | │ 🔗 地址: https://github.com/Elizabeth819/GPT4Video-cobra-auto │
46 | │ 🔴 高危 发现 1 个问题 │
47 | ╰──────────────────────────────────────────────────────────────────────────────╯
48 |
49 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
50 | │
51 | │ 🔴 风险等级: 高危 - 立即处理
52 | │
53 | │ 📄 文件路径: VO.py
54 | │ 📍 行号: 57
55 | │
56 | │ 🔑 密钥类型: 🤖 OpenAI API Key
57 | │ 🔐 密钥内容: sk-V*******************************************S4Q2
58 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
59 | │
60 | │ 💻 代码片段:
61 | │ os.environ["OPENAI_API_KEY"] = "sk-V7SJB46yMJn4MR4yTTYyT3BlbkFJyxCPkVmAGaAT5KOOS
62 | │
63 | │ 🕐 发现时间: 2025-10-16 01:37:07
64 | │
65 | └──────────────────────────────────────────────────────────────────────────
66 |
67 |
68 |
69 | ╭──────────────────────────────────────────────────────────────────────────────╮
70 | │ 📦 仓库: Thadzy/LLMTTS │
71 | │ 🔗 地址: https://github.com/Thadzy/LLMTTS │
72 | │ 🔴 高危 发现 4 个问题 │
73 | ╰──────────────────────────────────────────────────────────────────────────────╯
74 |
75 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
76 | │
77 | │ 🔴 风险等级: 高危 - 立即处理
78 | │
79 | │ 📄 文件路径: ImproveTTS.py
80 | │ 📍 行号: 10
81 | │
82 | │ 🔑 密钥类型: 🤖 OpenAI API Key
83 | │ 🔐 密钥内容: sk-J*******************************************ewkU
84 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
85 | │
86 | │ 💻 代码片段:
87 | │ os.environ["OPENAI_API_KEY"] = "sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQHoWe
88 | │
89 | │ 🕐 发现时间: 2025-10-16 01:39:22
90 | │
91 | └──────────────────────────────────────────────────────────────────────────
92 |
93 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
94 | │
95 | │ 🔴 风险等级: 高危 - 立即处理
96 | │
97 | │ 📄 文件路径: d.py
98 | │ 📍 行号: 12
99 | │
100 | │ 🔑 密钥类型: 🤖 OpenAI API Key
101 | │ 🔐 密钥内容: sk-J*******************************************ewkU
102 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
103 | │
104 | │ 💻 代码片段:
105 | │ os.environ["OPENAI_API_KEY"] = "sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQHoWe
106 | │
107 | │ 🕐 发现时间: 2025-10-16 01:39:22
108 | │
109 | └──────────────────────────────────────────────────────────────────────────
110 |
111 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
112 | │
113 | │ 🔴 风险等级: 高危 - 立即处理
114 | │
115 | │ 📄 文件路径: dwa.ipynb
116 | │ 📍 行号: 20
117 | │
118 | │ 🔑 密钥类型: 🤖 OpenAI API Key
119 | │ 🔐 密钥内容: sk-J*******************************************ewkU
120 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
121 | │
122 | │ 💻 代码片段:
123 | │ "os.environ[\"OPENAI_API_KEY\"] = \"sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQ
124 | │
125 | │ 🕐 发现时间: 2025-10-16 01:39:22
126 | │
127 | └──────────────────────────────────────────────────────────────────────────
128 |
129 | ┌─ 问题 #4 ──────────────────────────────────────────────────────────────────
130 | │
131 | │ 🔴 风险等级: 高危 - 立即处理
132 | │
133 | │ 📄 文件路径: streamlit.py
134 | │ 📍 行号: 11
135 | │
136 | │ 🔑 密钥类型: 🤖 OpenAI API Key
137 | │ 🔐 密钥内容: sk-J*******************************************ewkU
138 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
139 | │
140 | │ 💻 代码片段:
141 | │ os.environ["OPENAI_API_KEY"] = "sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQHoWe
142 | │
143 | │ 🕐 发现时间: 2025-10-16 01:39:22
144 | │
145 | └──────────────────────────────────────────────────────────────────────────
146 |
147 |
148 |
149 | ╭──────────────────────────────────────────────────────────────────────────────╮
150 | │ 📦 仓库: tnnd114514/Neuro-finetune │
151 | │ 🔗 地址: https://github.com/tnnd114514/Neuro-finetune │
152 | │ 🔴 高危 发现 18 个问题 │
153 | ╰──────────────────────────────────────────────────────────────────────────────╯
154 |
155 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
156 | │
157 | │ 🔴 风险等级: 高危 - 立即处理
158 | │
159 | │ 📄 文件路径: 1.py
160 | │ 📍 行号: 18
161 | │
162 | │ 🔑 密钥类型: 🤖 OpenAI API Key
163 | │ 🔐 密钥内容: sk-i*******************************************dzSB
164 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
165 | │
166 | │ 💻 代码片段:
167 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
168 | │
169 | │ 🕐 发现时间: 2025-10-16 01:45:39
170 | │
171 | └──────────────────────────────────────────────────────────────────────────
172 |
173 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
174 | │
175 | │ 🔴 风险等级: 高危 - 立即处理
176 | │
177 | │ 📄 文件路径: 1.py
178 | │ 📍 行号: 44
179 | │
180 | │ 🔑 密钥类型: 🤖 OpenAI API Key
181 | │ 🔐 密钥内容: sk-l*******************************************eeOB
182 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
183 | │
184 | │ 💻 代码片段:
185 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
186 | │
187 | │ 🕐 发现时间: 2025-10-16 01:45:39
188 | │
189 | └──────────────────────────────────────────────────────────────────────────
190 |
191 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
192 | │
193 | │ 🔴 风险等级: 高危 - 立即处理
194 | │
195 | │ 📄 文件路径: 2.py
196 | │ 📍 行号: 18
197 | │
198 | │ 🔑 密钥类型: 🤖 OpenAI API Key
199 | │ 🔐 密钥内容: sk-i*******************************************dzSB
200 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
201 | │
202 | │ 💻 代码片段:
203 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
204 | │
205 | │ 🕐 发现时间: 2025-10-16 01:45:39
206 | │
207 | └──────────────────────────────────────────────────────────────────────────
208 |
209 | ┌─ 问题 #4 ──────────────────────────────────────────────────────────────────
210 | │
211 | │ 🔴 风险等级: 高危 - 立即处理
212 | │
213 | │ 📄 文件路径: 2.py
214 | │ 📍 行号: 44
215 | │
216 | │ 🔑 密钥类型: 🤖 OpenAI API Key
217 | │ 🔐 密钥内容: sk-l*******************************************eeOB
218 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
219 | │
220 | │ 💻 代码片段:
221 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
222 | │
223 | │ 🕐 发现时间: 2025-10-16 01:45:39
224 | │
225 | └──────────────────────────────────────────────────────────────────────────
226 |
227 | ┌─ 问题 #5 ──────────────────────────────────────────────────────────────────
228 | │
229 | │ 🔴 风险等级: 高危 - 立即处理
230 | │
231 | │ 📄 文件路径: 3.py
232 | │ 📍 行号: 18
233 | │
234 | │ 🔑 密钥类型: 🤖 OpenAI API Key
235 | │ 🔐 密钥内容: sk-i*******************************************dzSB
236 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
237 | │
238 | │ 💻 代码片段:
239 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
240 | │
241 | │ 🕐 发现时间: 2025-10-16 01:45:39
242 | │
243 | └──────────────────────────────────────────────────────────────────────────
244 |
245 | ┌─ 问题 #6 ──────────────────────────────────────────────────────────────────
246 | │
247 | │ 🔴 风险等级: 高危 - 立即处理
248 | │
249 | │ 📄 文件路径: 3.py
250 | │ 📍 行号: 44
251 | │
252 | │ 🔑 密钥类型: 🤖 OpenAI API Key
253 | │ 🔐 密钥内容: sk-l*******************************************eeOB
254 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
255 | │
256 | │ 💻 代码片段:
257 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
258 | │
259 | │ 🕐 发现时间: 2025-10-16 01:45:39
260 | │
261 | └──────────────────────────────────────────────────────────────────────────
262 |
263 | ┌─ 问题 #7 ──────────────────────────────────────────────────────────────────
264 | │
265 | │ 🔴 风险等级: 高危 - 立即处理
266 | │
267 | │ 📄 文件路径: 4.py
268 | │ 📍 行号: 21
269 | │
270 | │ 🔑 密钥类型: 🤖 OpenAI API Key
271 | │ 🔐 密钥内容: sk-i*******************************************dzSB
272 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
273 | │
274 | │ 💻 代码片段:
275 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
276 | │
277 | │ 🕐 发现时间: 2025-10-16 01:45:39
278 | │
279 | └──────────────────────────────────────────────────────────────────────────
280 |
281 | ┌─ 问题 #8 ──────────────────────────────────────────────────────────────────
282 | │
283 | │ 🔴 风险等级: 高危 - 立即处理
284 | │
285 | │ 📄 文件路径: 4.py
286 | │ 📍 行号: 28
287 | │
288 | │ 🔑 密钥类型: 🤖 OpenAI API Key
289 | │ 🔐 密钥内容: sk-l*******************************************eeOB
290 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
291 | │
292 | │ 💻 代码片段:
293 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
294 | │
295 | │ 🕐 发现时间: 2025-10-16 01:45:39
296 | │
297 | └──────────────────────────────────────────────────────────────────────────
298 |
299 | ┌─ 问题 #9 ──────────────────────────────────────────────────────────────────
300 | │
301 | │ 🔴 风险等级: 高危 - 立即处理
302 | │
303 | │ 📄 文件路径: 5.py
304 | │ 📍 行号: 18
305 | │
306 | │ 🔑 密钥类型: 🤖 OpenAI API Key
307 | │ 🔐 密钥内容: sk-i*******************************************dzSB
308 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
309 | │
310 | │ 💻 代码片段:
311 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
312 | │
313 | │ 🕐 发现时间: 2025-10-16 01:45:39
314 | │
315 | └──────────────────────────────────────────────────────────────────────────
316 |
317 | ┌─ 问题 #10 ──────────────────────────────────────────────────────────────────
318 | │
319 | │ 🔴 风险等级: 高危 - 立即处理
320 | │
321 | │ 📄 文件路径: 5.py
322 | │ 📍 行号: 42
323 | │
324 | │ 🔑 密钥类型: 🤖 OpenAI API Key
325 | │ 🔐 密钥内容: sk-l*******************************************eeOB
326 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
327 | │
328 | │ 💻 代码片段:
329 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
330 | │
331 | │ 🕐 发现时间: 2025-10-16 01:45:39
332 | │
333 | └──────────────────────────────────────────────────────────────────────────
334 |
335 | ┌─ 问题 #11 ──────────────────────────────────────────────────────────────────
336 | │
337 | │ 🔴 风险等级: 高危 - 立即处理
338 | │
339 | │ 📄 文件路径: 6.py
340 | │ 📍 行号: 18
341 | │
342 | │ 🔑 密钥类型: 🤖 OpenAI API Key
343 | │ 🔐 密钥内容: sk-i*******************************************dzSB
344 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
345 | │
346 | │ 💻 代码片段:
347 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
348 | │
349 | │ 🕐 发现时间: 2025-10-16 01:45:39
350 | │
351 | └──────────────────────────────────────────────────────────────────────────
352 |
353 | ┌─ 问题 #12 ──────────────────────────────────────────────────────────────────
354 | │
355 | │ 🔴 风险等级: 高危 - 立即处理
356 | │
357 | │ 📄 文件路径: 6.py
358 | │ 📍 行号: 42
359 | │
360 | │ 🔑 密钥类型: 🤖 OpenAI API Key
361 | │ 🔐 密钥内容: sk-l*******************************************eeOB
362 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
363 | │
364 | │ 💻 代码片段:
365 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
366 | │
367 | │ 🕐 发现时间: 2025-10-16 01:45:39
368 | │
369 | └──────────────────────────────────────────────────────────────────────────
370 |
371 | ┌─ 问题 #13 ──────────────────────────────────────────────────────────────────
372 | │
373 | │ 🔴 风险等级: 高危 - 立即处理
374 | │
375 | │ 📄 文件路径: 7.py
376 | │ 📍 行号: 18
377 | │
378 | │ 🔑 密钥类型: 🤖 OpenAI API Key
379 | │ 🔐 密钥内容: sk-i*******************************************dzSB
380 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
381 | │
382 | │ 💻 代码片段:
383 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
384 | │
385 | │ 🕐 发现时间: 2025-10-16 01:45:39
386 | │
387 | └──────────────────────────────────────────────────────────────────────────
388 |
389 | ┌─ 问题 #14 ──────────────────────────────────────────────────────────────────
390 | │
391 | │ 🔴 风险等级: 高危 - 立即处理
392 | │
393 | │ 📄 文件路径: 7.py
394 | │ 📍 行号: 42
395 | │
396 | │ 🔑 密钥类型: 🤖 OpenAI API Key
397 | │ 🔐 密钥内容: sk-l*******************************************eeOB
398 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
399 | │
400 | │ 💻 代码片段:
401 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
402 | │
403 | │ 🕐 发现时间: 2025-10-16 01:45:39
404 | │
405 | └──────────────────────────────────────────────────────────────────────────
406 |
407 | ┌─ 问题 #15 ──────────────────────────────────────────────────────────────────
408 | │
409 | │ 🔴 风险等级: 高危 - 立即处理
410 | │
411 | │ 📄 文件路径: 8.py
412 | │ 📍 行号: 19
413 | │
414 | │ 🔑 密钥类型: 🤖 OpenAI API Key
415 | │ 🔐 密钥内容: sk-i*******************************************dzSB
416 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
417 | │
418 | │ 💻 代码片段:
419 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
420 | │
421 | │ 🕐 发现时间: 2025-10-16 01:45:39
422 | │
423 | └──────────────────────────────────────────────────────────────────────────
424 |
425 | ┌─ 问题 #16 ──────────────────────────────────────────────────────────────────
426 | │
427 | │ 🔴 风险等级: 高危 - 立即处理
428 | │
429 | │ 📄 文件路径: aichat.py
430 | │ 📍 行号: 18
431 | │
432 | │ 🔑 密钥类型: 🤖 OpenAI API Key
433 | │ 🔐 密钥内容: sk-i*******************************************dzSB
434 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
435 | │
436 | │ 💻 代码片段:
437 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
438 | │
439 | │ 🕐 发现时间: 2025-10-16 01:45:39
440 | │
441 | └──────────────────────────────────────────────────────────────────────────
442 |
443 | ┌─ 问题 #17 ──────────────────────────────────────────────────────────────────
444 | │
445 | │ 🔴 风险等级: 高危 - 立即处理
446 | │
447 | │ 📄 文件路径: aichat.py
448 | │ 📍 行号: 44
449 | │
450 | │ 🔑 密钥类型: 🤖 OpenAI API Key
451 | │ 🔐 密钥内容: sk-l*******************************************eeOB
452 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
453 | │
454 | │ 💻 代码片段:
455 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
456 | │
457 | │ 🕐 发现时间: 2025-10-16 01:45:39
458 | │
459 | └──────────────────────────────────────────────────────────────────────────
460 |
461 | ┌─ 问题 #18 ──────────────────────────────────────────────────────────────────
462 | │
463 | │ 🔴 风险等级: 高危 - 立即处理
464 | │
465 | │ 📄 文件路径: n.py
466 | │ 📍 行号: 12
467 | │
468 | │ 🔑 密钥类型: 🤖 OpenAI API Key
469 | │ 🔐 密钥内容: sk-l*******************************************eeOB
470 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
471 | │
472 | │ 💻 代码片段:
473 | │ CUSTOM_API_KEY = "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB" # 替换为服务商
474 | │
475 | │ 🕐 发现时间: 2025-10-16 01:45:39
476 | │
477 | └──────────────────────────────────────────────────────────────────────────
478 |
479 |
480 |
481 | ╭──────────────────────────────────────────────────────────────────────────────╮
482 | │ 📦 仓库: nihark023/Gen_AI_model │
483 | │ 🔗 地址: https://github.com/nihark023/Gen_AI_model │
484 | │ 🔴 高危 发现 1 个问题 │
485 | ╰──────────────────────────────────────────────────────────────────────────────╯
486 |
487 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
488 | │
489 | │ 🔴 风险等级: 高危 - 立即处理
490 | │
491 | │ 📄 文件路径: .env
492 | │ 📍 行号: 1
493 | │
494 | │ 🔑 密钥类型: 🤖 OpenAI API Key
495 | │ 🔐 密钥内容: sk-a***********************************ef12
496 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
497 | │
498 | │ 💻 代码片段:
499 | │ OPENAI_API_KEY="sk-abcdef1234567890abcdef1234567890abcdef12"
500 | │
501 | │ 🕐 发现时间: 2025-10-16 01:46:15
502 | │
503 | └──────────────────────────────────────────────────────────────────────────
504 |
505 |
506 |
507 | ╭──────────────────────────────────────────────────────────────────────────────╮
508 | │ 📦 仓库: wanru122/midtern │
509 | │ 🔗 地址: https://github.com/wanru122/midtern │
510 | │ 🟡 中危 发现 1 个问题 │
511 | ╰──────────────────────────────────────────────────────────────────────────────╯
512 |
513 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
514 | │
515 | │ 🟡 风险等级: 中危 - 尽快处理
516 | │
517 | │ 📄 文件路径: .env
518 | │ 📍 行号: 13
519 | │
520 | │ 🔑 密钥类型: 🔑 通用 API Key
521 | │ 🔐 密钥内容: apiK***************************************************************************************qN8'
522 | │ 🎯 匹配规则: 📌 apiKey 对象属性/变量赋值
523 | │
524 | │ 💻 代码片段:
525 | │ apiKey = 'G7ntjQXvYJ59h7q1EJWrErTyjJwAMTo6F8mKEC1gglF9yB28JDN7JQQJ99AJACYeBjFXJ3
526 | │
527 | │ 🕐 发现时间: 2025-10-16 01:47:08
528 | │
529 | └──────────────────────────────────────────────────────────────────────────
530 |
531 |
532 |
533 | ╔══════════════════════════════════════════════════════════════════════════════╗
534 | ║ ║
535 | ║ 📊 统计信息与分析 ║
536 | ║ ║
537 | ╚══════════════════════════════════════════════════════════════════════════════╝
538 |
539 | ┌─ 风险等级分布
540 | │
541 | │ 🔴 高危问题: 25 个 ( 96.2%) ███████████████████
542 | │ 🟡 中危问题: 1 个 ( 3.8%)
543 | │ 🟢 低危问题: 0 个 ( 0.0%)
544 | │
545 | │ 📊 总计: 26 个潜在问题
546 | └──────────────────────────────────────────────────────────────────────────────
547 |
548 | ┌─ 影响范围
549 | │
550 | │ 📦 涉及仓库: 6 个
551 | │ 📄 涉及文件: 16 个
552 | │
553 | └──────────────────────────────────────────────────────────────────────────────
554 |
555 | ┌─ 密钥类型分布
556 | │
557 | │ 🤖 OpenAI API Key: 25 个
558 | │ 🔑 通用 API Key: 1 个
559 | │
560 | └──────────────────────────────────────────────────────────────────────────────
561 |
562 | ╔══════════════════════════════════════════════════════════════════════════════╗
563 | ║ 🛡️ 安全建议 ║
564 | ╚══════════════════════════════════════════════════════════════════════════════╝
565 |
566 | ⚠️ 立即行动(针对高危问题):
567 | 1. 🚨 立即撤销/轮换所有泄露的 API 密钥
568 | 2. 🔍 检查 API 使用日志,确认是否被滥用
569 | 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)
570 | 4. 📧 通知相关团队成员
571 |
572 | 🔒 长期防护措施:
573 | 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)
574 | 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件
575 | 3. 🪝 配置 pre-commit hooks 防止敏感信息提交
576 | 4. 🔄 定期轮换 API 密钥
577 | 5. 👥 对团队进行安全培训
578 | 6. 📊 定期运行此扫描工具进行审查
579 |
580 | 📚 参考资源:
581 | • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning
582 | • Git 历史清理: https://github.com/newren/git-filter-repo
583 | • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
584 |
585 | ╔══════════════════════════════════════════════════════════════════════════════╗
586 | ║ ║
587 | ║ ✅ 报告生成完成 - 请及时处理发现的问题 ║
588 | ║ ║
589 | ║ 生成时间: 2025年10月16日 01:55:25 ║
590 | ║ 报告位置: ./scan_reports/scan_report_20251016_015525.txt ║
591 | ║ ║
592 | ╚══════════════════════════════════════════════════════════════════════════════╝
593 |
--------------------------------------------------------------------------------
/scan_reports/scan_report_20251010_035942.txt:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════════════════════════════════════╗
2 | ║ ║
3 | ║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
4 | ║ ║
5 | ╚══════════════════════════════════════════════════════════════════════════════╝
6 |
7 | 📋 扫描信息
8 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9 | 🎯 扫描类型: 🤖 自动搜索 AI 项目
10 | ⏱️ 开始时间: 2025-10-10 02:59:03
11 | ⏱️ 结束时间: 2025-10-10 03:59:42
12 | ⏳ 扫描耗时: 60分39秒
13 | 🔴 发现问题数: 27 个 (🔴 26 高危, 🟡 1 中危)
14 | 📦 涉及仓库数: 5 个
15 |
16 |
17 | ╭──────────────────────────────────────────────────────────────────────────────╮
18 | │ 📦 仓库: Thadzy/LLMTTS │
19 | │ 🔗 地址: https://github.com/Thadzy/LLMTTS │
20 | │ 🔴 高危 发现 4 个问题 │
21 | ╰──────────────────────────────────────────────────────────────────────────────╯
22 |
23 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
24 | │
25 | │ 🔴 风险等级: 高危 - 立即处理
26 | │
27 | │ 📄 文件路径: ImproveTTS.py
28 | │ 📍 行号: 10
29 | │
30 | │ 🔑 密钥类型: 🤖 OpenAI API Key
31 | │ 🔐 密钥内容: sk-J*******************************************ewkU
32 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
33 | │
34 | │ 💻 代码片段:
35 | │ os.environ["OPENAI_API_KEY"] = "sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQHoWe
36 | │
37 | │ 🕐 发现时间: 2025-10-10 02:59:29
38 | │
39 | └──────────────────────────────────────────────────────────────────────────
40 |
41 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
42 | │
43 | │ 🔴 风险等级: 高危 - 立即处理
44 | │
45 | │ 📄 文件路径: d.py
46 | │ 📍 行号: 12
47 | │
48 | │ 🔑 密钥类型: 🤖 OpenAI API Key
49 | │ 🔐 密钥内容: sk-J*******************************************ewkU
50 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
51 | │
52 | │ 💻 代码片段:
53 | │ os.environ["OPENAI_API_KEY"] = "sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQHoWe
54 | │
55 | │ 🕐 发现时间: 2025-10-10 02:59:29
56 | │
57 | └──────────────────────────────────────────────────────────────────────────
58 |
59 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
60 | │
61 | │ 🔴 风险等级: 高危 - 立即处理
62 | │
63 | │ 📄 文件路径: dwa.ipynb
64 | │ 📍 行号: 20
65 | │
66 | │ 🔑 密钥类型: 🤖 OpenAI API Key
67 | │ 🔐 密钥内容: sk-J*******************************************ewkU
68 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
69 | │
70 | │ 💻 代码片段:
71 | │ "os.environ[\"OPENAI_API_KEY\"] = \"sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQ
72 | │
73 | │ 🕐 发现时间: 2025-10-10 02:59:29
74 | │
75 | └──────────────────────────────────────────────────────────────────────────
76 |
77 | ┌─ 问题 #4 ──────────────────────────────────────────────────────────────────
78 | │
79 | │ 🔴 风险等级: 高危 - 立即处理
80 | │
81 | │ 📄 文件路径: streamlit.py
82 | │ 📍 行号: 11
83 | │
84 | │ 🔑 密钥类型: 🤖 OpenAI API Key
85 | │ 🔐 密钥内容: sk-J*******************************************ewkU
86 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
87 | │
88 | │ 💻 代码片段:
89 | │ os.environ["OPENAI_API_KEY"] = "sk-JoABD96Rui1GWSrpJ6h78lTDEkbQG386TWBYZWLPQHoWe
90 | │
91 | │ 🕐 发现时间: 2025-10-10 02:59:29
92 | │
93 | └──────────────────────────────────────────────────────────────────────────
94 |
95 |
96 |
97 | ╭──────────────────────────────────────────────────────────────────────────────╮
98 | │ 📦 仓库: tnnd114514/Neuro-finetune │
99 | │ 🔗 地址: https://github.com/tnnd114514/Neuro-finetune │
100 | │ 🔴 高危 发现 18 个问题 │
101 | ╰──────────────────────────────────────────────────────────────────────────────╯
102 |
103 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
104 | │
105 | │ 🔴 风险等级: 高危 - 立即处理
106 | │
107 | │ 📄 文件路径: 1.py
108 | │ 📍 行号: 18
109 | │
110 | │ 🔑 密钥类型: 🤖 OpenAI API Key
111 | │ 🔐 密钥内容: sk-i*******************************************dzSB
112 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
113 | │
114 | │ 💻 代码片段:
115 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
116 | │
117 | │ 🕐 发现时间: 2025-10-10 03:05:22
118 | │
119 | └──────────────────────────────────────────────────────────────────────────
120 |
121 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
122 | │
123 | │ 🔴 风险等级: 高危 - 立即处理
124 | │
125 | │ 📄 文件路径: 1.py
126 | │ 📍 行号: 44
127 | │
128 | │ 🔑 密钥类型: 🤖 OpenAI API Key
129 | │ 🔐 密钥内容: sk-l*******************************************eeOB
130 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
131 | │
132 | │ 💻 代码片段:
133 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
134 | │
135 | │ 🕐 发现时间: 2025-10-10 03:05:22
136 | │
137 | └──────────────────────────────────────────────────────────────────────────
138 |
139 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
140 | │
141 | │ 🔴 风险等级: 高危 - 立即处理
142 | │
143 | │ 📄 文件路径: 2.py
144 | │ 📍 行号: 18
145 | │
146 | │ 🔑 密钥类型: 🤖 OpenAI API Key
147 | │ 🔐 密钥内容: sk-i*******************************************dzSB
148 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
149 | │
150 | │ 💻 代码片段:
151 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
152 | │
153 | │ 🕐 发现时间: 2025-10-10 03:05:22
154 | │
155 | └──────────────────────────────────────────────────────────────────────────
156 |
157 | ┌─ 问题 #4 ──────────────────────────────────────────────────────────────────
158 | │
159 | │ 🔴 风险等级: 高危 - 立即处理
160 | │
161 | │ 📄 文件路径: 2.py
162 | │ 📍 行号: 44
163 | │
164 | │ 🔑 密钥类型: 🤖 OpenAI API Key
165 | │ 🔐 密钥内容: sk-l*******************************************eeOB
166 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
167 | │
168 | │ 💻 代码片段:
169 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
170 | │
171 | │ 🕐 发现时间: 2025-10-10 03:05:22
172 | │
173 | └──────────────────────────────────────────────────────────────────────────
174 |
175 | ┌─ 问题 #5 ──────────────────────────────────────────────────────────────────
176 | │
177 | │ 🔴 风险等级: 高危 - 立即处理
178 | │
179 | │ 📄 文件路径: 3.py
180 | │ 📍 行号: 18
181 | │
182 | │ 🔑 密钥类型: 🤖 OpenAI API Key
183 | │ 🔐 密钥内容: sk-i*******************************************dzSB
184 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
185 | │
186 | │ 💻 代码片段:
187 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
188 | │
189 | │ 🕐 发现时间: 2025-10-10 03:05:22
190 | │
191 | └──────────────────────────────────────────────────────────────────────────
192 |
193 | ┌─ 问题 #6 ──────────────────────────────────────────────────────────────────
194 | │
195 | │ 🔴 风险等级: 高危 - 立即处理
196 | │
197 | │ 📄 文件路径: 3.py
198 | │ 📍 行号: 44
199 | │
200 | │ 🔑 密钥类型: 🤖 OpenAI API Key
201 | │ 🔐 密钥内容: sk-l*******************************************eeOB
202 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
203 | │
204 | │ 💻 代码片段:
205 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
206 | │
207 | │ 🕐 发现时间: 2025-10-10 03:05:22
208 | │
209 | └──────────────────────────────────────────────────────────────────────────
210 |
211 | ┌─ 问题 #7 ──────────────────────────────────────────────────────────────────
212 | │
213 | │ 🔴 风险等级: 高危 - 立即处理
214 | │
215 | │ 📄 文件路径: 4.py
216 | │ 📍 行号: 21
217 | │
218 | │ 🔑 密钥类型: 🤖 OpenAI API Key
219 | │ 🔐 密钥内容: sk-i*******************************************dzSB
220 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
221 | │
222 | │ 💻 代码片段:
223 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
224 | │
225 | │ 🕐 发现时间: 2025-10-10 03:05:22
226 | │
227 | └──────────────────────────────────────────────────────────────────────────
228 |
229 | ┌─ 问题 #8 ──────────────────────────────────────────────────────────────────
230 | │
231 | │ 🔴 风险等级: 高危 - 立即处理
232 | │
233 | │ 📄 文件路径: 4.py
234 | │ 📍 行号: 28
235 | │
236 | │ 🔑 密钥类型: 🤖 OpenAI API Key
237 | │ 🔐 密钥内容: sk-l*******************************************eeOB
238 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
239 | │
240 | │ 💻 代码片段:
241 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
242 | │
243 | │ 🕐 发现时间: 2025-10-10 03:05:22
244 | │
245 | └──────────────────────────────────────────────────────────────────────────
246 |
247 | ┌─ 问题 #9 ──────────────────────────────────────────────────────────────────
248 | │
249 | │ 🔴 风险等级: 高危 - 立即处理
250 | │
251 | │ 📄 文件路径: 5.py
252 | │ 📍 行号: 18
253 | │
254 | │ 🔑 密钥类型: 🤖 OpenAI API Key
255 | │ 🔐 密钥内容: sk-i*******************************************dzSB
256 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
257 | │
258 | │ 💻 代码片段:
259 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
260 | │
261 | │ 🕐 发现时间: 2025-10-10 03:05:22
262 | │
263 | └──────────────────────────────────────────────────────────────────────────
264 |
265 | ┌─ 问题 #10 ──────────────────────────────────────────────────────────────────
266 | │
267 | │ 🔴 风险等级: 高危 - 立即处理
268 | │
269 | │ 📄 文件路径: 5.py
270 | │ 📍 行号: 42
271 | │
272 | │ 🔑 密钥类型: 🤖 OpenAI API Key
273 | │ 🔐 密钥内容: sk-l*******************************************eeOB
274 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
275 | │
276 | │ 💻 代码片段:
277 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
278 | │
279 | │ 🕐 发现时间: 2025-10-10 03:05:22
280 | │
281 | └──────────────────────────────────────────────────────────────────────────
282 |
283 | ┌─ 问题 #11 ──────────────────────────────────────────────────────────────────
284 | │
285 | │ 🔴 风险等级: 高危 - 立即处理
286 | │
287 | │ 📄 文件路径: 6.py
288 | │ 📍 行号: 18
289 | │
290 | │ 🔑 密钥类型: 🤖 OpenAI API Key
291 | │ 🔐 密钥内容: sk-i*******************************************dzSB
292 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
293 | │
294 | │ 💻 代码片段:
295 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
296 | │
297 | │ 🕐 发现时间: 2025-10-10 03:05:22
298 | │
299 | └──────────────────────────────────────────────────────────────────────────
300 |
301 | ┌─ 问题 #12 ──────────────────────────────────────────────────────────────────
302 | │
303 | │ 🔴 风险等级: 高危 - 立即处理
304 | │
305 | │ 📄 文件路径: 6.py
306 | │ 📍 行号: 42
307 | │
308 | │ 🔑 密钥类型: 🤖 OpenAI API Key
309 | │ 🔐 密钥内容: sk-l*******************************************eeOB
310 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
311 | │
312 | │ 💻 代码片段:
313 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
314 | │
315 | │ 🕐 发现时间: 2025-10-10 03:05:22
316 | │
317 | └──────────────────────────────────────────────────────────────────────────
318 |
319 | ┌─ 问题 #13 ──────────────────────────────────────────────────────────────────
320 | │
321 | │ 🔴 风险等级: 高危 - 立即处理
322 | │
323 | │ 📄 文件路径: 7.py
324 | │ 📍 行号: 18
325 | │
326 | │ 🔑 密钥类型: 🤖 OpenAI API Key
327 | │ 🔐 密钥内容: sk-i*******************************************dzSB
328 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
329 | │
330 | │ 💻 代码片段:
331 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
332 | │
333 | │ 🕐 发现时间: 2025-10-10 03:05:22
334 | │
335 | └──────────────────────────────────────────────────────────────────────────
336 |
337 | ┌─ 问题 #14 ──────────────────────────────────────────────────────────────────
338 | │
339 | │ 🔴 风险等级: 高危 - 立即处理
340 | │
341 | │ 📄 文件路径: 7.py
342 | │ 📍 行号: 42
343 | │
344 | │ 🔑 密钥类型: 🤖 OpenAI API Key
345 | │ 🔐 密钥内容: sk-l*******************************************eeOB
346 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
347 | │
348 | │ 💻 代码片段:
349 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
350 | │
351 | │ 🕐 发现时间: 2025-10-10 03:05:22
352 | │
353 | └──────────────────────────────────────────────────────────────────────────
354 |
355 | ┌─ 问题 #15 ──────────────────────────────────────────────────────────────────
356 | │
357 | │ 🔴 风险等级: 高危 - 立即处理
358 | │
359 | │ 📄 文件路径: 8.py
360 | │ 📍 行号: 19
361 | │
362 | │ 🔑 密钥类型: 🤖 OpenAI API Key
363 | │ 🔐 密钥内容: sk-i*******************************************dzSB
364 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
365 | │
366 | │ 💻 代码片段:
367 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
368 | │
369 | │ 🕐 发现时间: 2025-10-10 03:05:22
370 | │
371 | └──────────────────────────────────────────────────────────────────────────
372 |
373 | ┌─ 问题 #16 ──────────────────────────────────────────────────────────────────
374 | │
375 | │ 🔴 风险等级: 高危 - 立即处理
376 | │
377 | │ 📄 文件路径: aichat.py
378 | │ 📍 行号: 18
379 | │
380 | │ 🔑 密钥类型: 🤖 OpenAI API Key
381 | │ 🔐 密钥内容: sk-i*******************************************dzSB
382 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
383 | │
384 | │ 💻 代码片段:
385 | │ "api_key": "sk-iVZ3N2WBXyUGJLOLbJXgVbZbRcIwrNAXPKlUpZOF4IKrdzSB",
386 | │
387 | │ 🕐 发现时间: 2025-10-10 03:05:22
388 | │
389 | └──────────────────────────────────────────────────────────────────────────
390 |
391 | ┌─ 问题 #17 ──────────────────────────────────────────────────────────────────
392 | │
393 | │ 🔴 风险等级: 高危 - 立即处理
394 | │
395 | │ 📄 文件路径: aichat.py
396 | │ 📍 行号: 44
397 | │
398 | │ 🔑 密钥类型: 🤖 OpenAI API Key
399 | │ 🔐 密钥内容: sk-l*******************************************eeOB
400 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
401 | │
402 | │ 💻 代码片段:
403 | │ "api_key": "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB",
404 | │
405 | │ 🕐 发现时间: 2025-10-10 03:05:22
406 | │
407 | └──────────────────────────────────────────────────────────────────────────
408 |
409 | ┌─ 问题 #18 ──────────────────────────────────────────────────────────────────
410 | │
411 | │ 🔴 风险等级: 高危 - 立即处理
412 | │
413 | │ 📄 文件路径: n.py
414 | │ 📍 行号: 12
415 | │
416 | │ 🔑 密钥类型: 🤖 OpenAI API Key
417 | │ 🔐 密钥内容: sk-l*******************************************eeOB
418 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
419 | │
420 | │ 💻 代码片段:
421 | │ CUSTOM_API_KEY = "sk-lCsmD3pXeLBV7MbA9IkhY0vRbYEb6BJQlEiFU3JaHyoheeOB" # 替换为服务商
422 | │
423 | │ 🕐 发现时间: 2025-10-10 03:05:22
424 | │
425 | └──────────────────────────────────────────────────────────────────────────
426 |
427 |
428 |
429 | ╭──────────────────────────────────────────────────────────────────────────────╮
430 | │ 📦 仓库: nihark023/Gen_AI_model │
431 | │ 🔗 地址: https://github.com/nihark023/Gen_AI_model │
432 | │ 🔴 高危 发现 1 个问题 │
433 | ╰──────────────────────────────────────────────────────────────────────────────╯
434 |
435 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
436 | │
437 | │ 🔴 风险等级: 高危 - 立即处理
438 | │
439 | │ 📄 文件路径: .env
440 | │ 📍 行号: 1
441 | │
442 | │ 🔑 密钥类型: 🤖 OpenAI API Key
443 | │ 🔐 密钥内容: sk-a***********************************ef12
444 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
445 | │
446 | │ 💻 代码片段:
447 | │ OPENAI_API_KEY="sk-abcdef1234567890abcdef1234567890abcdef12"
448 | │
449 | │ 🕐 发现时间: 2025-10-10 03:05:57
450 | │
451 | └──────────────────────────────────────────────────────────────────────────
452 |
453 |
454 |
455 | ╭──────────────────────────────────────────────────────────────────────────────╮
456 | │ 📦 仓库: wanru122/midtern │
457 | │ 🔗 地址: https://github.com/wanru122/midtern │
458 | │ 🟡 中危 发现 1 个问题 │
459 | ╰──────────────────────────────────────────────────────────────────────────────╯
460 |
461 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
462 | │
463 | │ 🟡 风险等级: 中危 - 尽快处理
464 | │
465 | │ 📄 文件路径: .env
466 | │ 📍 行号: 13
467 | │
468 | │ 🔑 密钥类型: 🔑 通用 API Key
469 | │ 🔐 密钥内容: apiK***************************************************************************************qN8'
470 | │ 🎯 匹配规则: 📌 apiKey 对象属性/变量赋值
471 | │
472 | │ 💻 代码片段:
473 | │ apiKey = 'G7ntjQXvYJ59h7q1EJWrErTyjJwAMTo6F8mKEC1gglF9yB28JDN7JQQJ99AJACYeBjFXJ3
474 | │
475 | │ 🕐 发现时间: 2025-10-10 03:06:45
476 | │
477 | └──────────────────────────────────────────────────────────────────────────
478 |
479 |
480 |
481 | ╭──────────────────────────────────────────────────────────────────────────────╮
482 | │ 📦 仓库: jayalath-jknr/pdf-streamlit │
483 | │ 🔗 地址: https://github.com/jayalath-jknr/pdf-streamlit │
484 | │ 🔴 高危 发现 3 个问题 │
485 | ╰──────────────────────────────────────────────────────────────────────────────╯
486 |
487 | ┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
488 | │
489 | │ 🔴 风险等级: 高危 - 立即处理
490 | │
491 | │ 📄 文件路径: ap.py
492 | │ 📍 行号: 25
493 | │
494 | │ 🔑 密钥类型: 🤖 OpenAI API Key
495 | │ 🔐 密钥内容: sk-w*******************************************4gDS
496 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
497 | │
498 | │ 💻 代码片段:
499 | │ OPENAI_API_KEY = "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"
500 | │
501 | │ 🕐 发现时间: 2025-10-10 03:14:30
502 | │
503 | └──────────────────────────────────────────────────────────────────────────
504 |
505 | ┌─ 问题 #2 ──────────────────────────────────────────────────────────────────
506 | │
507 | │ 🔴 风险等级: 高危 - 立即处理
508 | │
509 | │ 📄 文件路径: app.py
510 | │ 📍 行号: 9
511 | │
512 | │ 🔑 密钥类型: 🤖 OpenAI API Key
513 | │ 🔐 密钥内容: sk-F*******************************************n0CX
514 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
515 | │
516 | │ 💻 代码片段:
517 | │ OPENAI_API_KEY = "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"
518 | │
519 | │ 🕐 发现时间: 2025-10-10 03:14:30
520 | │
521 | └──────────────────────────────────────────────────────────────────────────
522 |
523 | ┌─ 问题 #3 ──────────────────────────────────────────────────────────────────
524 | │
525 | │ 🔴 风险等级: 高危 - 立即处理
526 | │
527 | │ 📄 文件路径: appold2.py
528 | │ 📍 行号: 54
529 | │
530 | │ 🔑 密钥类型: 🤖 OpenAI API Key
531 | │ 🔐 密钥内容: sk-F*******************************************n0CX
532 | │ 🎯 匹配规则: 📌 OpenAI API Key 格式 (sk-...)
533 | │
534 | │ 💻 代码片段:
535 | │ embeddings = OpenAIEmbeddings(openai_api_key="sk-F7s3C15MRa7UiL1r1nAFT3BlbkFJIim
536 | │
537 | │ 🕐 发现时间: 2025-10-10 03:14:30
538 | │
539 | └──────────────────────────────────────────────────────────────────────────
540 |
541 |
542 |
543 | ╔══════════════════════════════════════════════════════════════════════════════╗
544 | ║ ║
545 | ║ 📊 统计信息与分析 ║
546 | ║ ║
547 | ╚══════════════════════════════════════════════════════════════════════════════╝
548 |
549 | ┌─ 风险等级分布
550 | │
551 | │ 🔴 高危问题: 26 个 ( 96.3%) ███████████████████
552 | │ 🟡 中危问题: 1 个 ( 3.7%)
553 | │ 🟢 低危问题: 0 个 ( 0.0%)
554 | │
555 | │ 📊 总计: 27 个潜在问题
556 | └──────────────────────────────────────────────────────────────────────────────
557 |
558 | ┌─ 影响范围
559 | │
560 | │ 📦 涉及仓库: 5 个
561 | │ 📄 涉及文件: 18 个
562 | │
563 | └──────────────────────────────────────────────────────────────────────────────
564 |
565 | ┌─ 密钥类型分布
566 | │
567 | │ 🤖 OpenAI API Key: 26 个
568 | │ 🔑 通用 API Key: 1 个
569 | │
570 | └──────────────────────────────────────────────────────────────────────────────
571 |
572 | ╔══════════════════════════════════════════════════════════════════════════════╗
573 | ║ 🛡️ 安全建议 ║
574 | ╚══════════════════════════════════════════════════════════════════════════════╝
575 |
576 | ⚠️ 立即行动(针对高危问题):
577 | 1. 🚨 立即撤销/轮换所有泄露的 API 密钥
578 | 2. 🔍 检查 API 使用日志,确认是否被滥用
579 | 3. 🗑️ 从 Git 历史中彻底删除敏感信息(使用 git-filter-repo)
580 | 4. 📧 通知相关团队成员
581 |
582 | 🔒 长期防护措施:
583 | 1. 📝 使用环境变量或密钥管理服务(如 AWS Secrets Manager)
584 | 2. 🚫 在 .gitignore 中添加 .env, config.json 等敏感文件
585 | 3. 🪝 配置 pre-commit hooks 防止敏感信息提交
586 | 4. 🔄 定期轮换 API 密钥
587 | 5. 👥 对团队进行安全培训
588 | 6. 📊 定期运行此扫描工具进行审查
589 |
590 | 📚 参考资源:
591 | • GitHub 密钥扫描: https://docs.github.com/cn/code-security/secret-scanning
592 | • Git 历史清理: https://github.com/newren/git-filter-repo
593 | • 最佳实践: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
594 |
595 | ╔══════════════════════════════════════════════════════════════════════════════╗
596 | ║ ║
597 | ║ ✅ 报告生成完成 - 请及时处理发现的问题 ║
598 | ║ ║
599 | ║ 生成时间: 2025年10月10日 03:59:42 ║
600 | ║ 报告位置: ./scan_reports/scan_report_20251010_035942.txt ║
601 | ║ ║
602 | ╚══════════════════════════════════════════════════════════════════════════════╝
603 |
--------------------------------------------------------------------------------
/scan_history/scanned_repos.json:
--------------------------------------------------------------------------------
1 | {
2 | "repos": {
3 | "timsterxp/DiscordBot": {
4 | "first_scan": "2025-10-16 01:32:05",
5 | "last_scan": "2025-10-16 01:32:05",
6 | "findings_count": 0,
7 | "scan_type": "auto:ai-projects",
8 | "scan_count": 1
9 | },
10 | "sunnysab/bot": {
11 | "first_scan": "2025-10-16 01:32:18",
12 | "last_scan": "2025-10-16 01:32:18",
13 | "findings_count": 0,
14 | "scan_type": "auto:ai-projects",
15 | "scan_count": 1
16 | },
17 | "thyeem/lm": {
18 | "first_scan": "2025-10-16 01:32:32",
19 | "last_scan": "2025-10-16 01:32:32",
20 | "findings_count": 0,
21 | "scan_type": "auto:ai-projects",
22 | "scan_count": 1
23 | },
24 | "shibing624/open-o1": {
25 | "first_scan": "2025-10-16 01:32:48",
26 | "last_scan": "2025-10-16 01:32:48",
27 | "findings_count": 0,
28 | "scan_type": "auto:ai-projects",
29 | "scan_count": 1
30 | },
31 | "SkyHusar/takeover-infinity-toolkit": {
32 | "first_scan": "2025-10-16 01:33:02",
33 | "last_scan": "2025-10-16 01:33:02",
34 | "findings_count": 0,
35 | "scan_type": "auto:ai-projects",
36 | "scan_count": 1
37 | },
38 | "parthgupta1208/VoiceCraft": {
39 | "first_scan": "2025-10-16 01:33:19",
40 | "last_scan": "2025-10-16 01:33:19",
41 | "findings_count": 0,
42 | "scan_type": "auto:ai-projects",
43 | "scan_count": 1
44 | },
45 | "himanshu662000/InfoGPT": {
46 | "first_scan": "2025-10-16 01:33:30",
47 | "last_scan": "2025-10-16 01:33:30",
48 | "findings_count": 0,
49 | "scan_type": "auto:ai-projects",
50 | "scan_count": 1
51 | },
52 | "pandruszkow/asq": {
53 | "first_scan": "2025-10-16 01:33:38",
54 | "last_scan": "2025-10-16 01:33:38",
55 | "findings_count": 0,
56 | "scan_type": "auto:ai-projects",
57 | "scan_count": 1
58 | },
59 | "ERR0RPR0MPT/WaifuStream": {
60 | "first_scan": "2025-10-16 01:35:00",
61 | "last_scan": "2025-10-16 01:35:00",
62 | "findings_count": 0,
63 | "scan_type": "auto:ai-projects",
64 | "scan_count": 1
65 | },
66 | "Kaludii/ChatGPT-Turbo-SMS": {
67 | "first_scan": "2025-10-16 01:35:04",
68 | "last_scan": "2025-10-16 01:35:04",
69 | "findings_count": 0,
70 | "scan_type": "auto:ai-projects",
71 | "scan_count": 1
72 | },
73 | "sangwu99/Simplot": {
74 | "first_scan": "2025-10-16 01:35:26",
75 | "last_scan": "2025-10-16 01:35:26",
76 | "findings_count": 0,
77 | "scan_type": "auto:ai-projects",
78 | "scan_count": 1
79 | },
80 | "Olney1/ChatGPT-OpenAI-Smart-Speaker": {
81 | "first_scan": "2025-10-16 01:36:04",
82 | "last_scan": "2025-10-16 01:36:04",
83 | "findings_count": 0,
84 | "scan_type": "auto:ai-projects",
85 | "scan_count": 1
86 | },
87 | "zxc3309/AI-note-taker": {
88 | "first_scan": "2025-10-16 01:36:07",
89 | "last_scan": "2025-10-16 01:36:07",
90 | "findings_count": 0,
91 | "scan_type": "auto:ai-projects",
92 | "scan_count": 1
93 | },
94 | "Aliyansayz/CSVGPT": {
95 | "first_scan": "2025-10-16 01:36:16",
96 | "last_scan": "2025-10-16 01:36:16",
97 | "findings_count": 1,
98 | "scan_type": "auto:ai-projects",
99 | "scan_count": 1
100 | },
101 | "PatrickR1993/youtube-ci-converter": {
102 | "first_scan": "2025-10-16 01:36:37",
103 | "last_scan": "2025-10-16 01:36:37",
104 | "findings_count": 0,
105 | "scan_type": "auto:ai-projects",
106 | "scan_count": 1
107 | },
108 | "vega-soft/LLM_PDF_QA_Bot": {
109 | "first_scan": "2025-10-16 01:36:40",
110 | "last_scan": "2025-10-16 01:36:40",
111 | "findings_count": 0,
112 | "scan_type": "auto:ai-projects",
113 | "scan_count": 1
114 | },
115 | "chungimungi/Basic-Sentiment-Analyzer": {
116 | "first_scan": "2025-10-16 01:36:44",
117 | "last_scan": "2025-10-16 01:36:44",
118 | "findings_count": 0,
119 | "scan_type": "auto:ai-projects",
120 | "scan_count": 1
121 | },
122 | "daniyaljais/p1": {
123 | "first_scan": "2025-10-16 01:36:51",
124 | "last_scan": "2025-10-16 01:36:51",
125 | "findings_count": 0,
126 | "scan_type": "auto:ai-projects",
127 | "scan_count": 1
128 | },
129 | "Kirneill/ytvid": {
130 | "first_scan": "2025-10-16 01:37:01",
131 | "last_scan": "2025-10-16 01:37:01",
132 | "findings_count": 0,
133 | "scan_type": "auto:ai-projects",
134 | "scan_count": 1
135 | },
136 | "Tarun0951/interview": {
137 | "first_scan": "2025-10-16 01:37:07",
138 | "last_scan": "2025-10-16 01:37:07",
139 | "findings_count": 0,
140 | "scan_type": "auto:ai-projects",
141 | "scan_count": 1
142 | },
143 | "Elizabeth819/GPT4Video-cobra-auto": {
144 | "first_scan": "2025-10-16 01:38:16",
145 | "last_scan": "2025-10-16 01:38:16",
146 | "findings_count": 1,
147 | "scan_type": "auto:ai-projects",
148 | "scan_count": 1
149 | },
150 | "VasileiosKarapoulios/RAG_LangChain": {
151 | "first_scan": "2025-10-16 01:38:23",
152 | "last_scan": "2025-10-16 01:38:23",
153 | "findings_count": 0,
154 | "scan_type": "auto:ai-projects:failed",
155 | "scan_count": 1
156 | },
157 | "fullthom/chat-gpt-quine": {
158 | "first_scan": "2025-10-16 01:39:18",
159 | "last_scan": "2025-10-16 01:39:18",
160 | "findings_count": 0,
161 | "scan_type": "auto:ai-projects",
162 | "scan_count": 1
163 | },
164 | "tk755/q": {
165 | "first_scan": "2025-10-16 01:39:22",
166 | "last_scan": "2025-10-16 01:39:22",
167 | "findings_count": 0,
168 | "scan_type": "auto:ai-projects",
169 | "scan_count": 1
170 | },
171 | "Thadzy/LLMTTS": {
172 | "first_scan": "2025-10-16 01:39:36",
173 | "last_scan": "2025-10-16 01:39:36",
174 | "findings_count": 4,
175 | "scan_type": "auto:ai-projects",
176 | "scan_count": 1
177 | },
178 | "Git-sit-go/Utilization11": {
179 | "first_scan": "2025-10-16 01:39:39",
180 | "last_scan": "2025-10-16 01:39:39",
181 | "findings_count": 0,
182 | "scan_type": "auto:ai-projects",
183 | "scan_count": 1
184 | },
185 | "Arash-Khajavi/Arash": {
186 | "first_scan": "2025-10-16 01:39:48",
187 | "last_scan": "2025-10-16 01:39:48",
188 | "findings_count": 0,
189 | "scan_type": "auto:ai-projects",
190 | "scan_count": 1
191 | },
192 | "souvikmaity797/aSk-mY-boT-": {
193 | "first_scan": "2025-10-16 01:45:39",
194 | "last_scan": "2025-10-16 01:45:39",
195 | "findings_count": 0,
196 | "scan_type": "auto:ai-projects",
197 | "scan_count": 1
198 | },
199 | "tnnd114514/Neuro-finetune": {
200 | "first_scan": "2025-10-16 01:46:08",
201 | "last_scan": "2025-10-16 01:46:08",
202 | "findings_count": 18,
203 | "scan_type": "auto:ai-projects",
204 | "scan_count": 1
205 | },
206 | "IzyZen/rvu-admissions-chatbot": {
207 | "first_scan": "2025-10-16 01:46:15",
208 | "last_scan": "2025-10-16 01:46:15",
209 | "findings_count": 0,
210 | "scan_type": "auto:ai-projects",
211 | "scan_count": 1
212 | },
213 | "nihark023/Gen_AI_model": {
214 | "first_scan": "2025-10-16 01:46:20",
215 | "last_scan": "2025-10-16 01:46:20",
216 | "findings_count": 1,
217 | "scan_type": "auto:ai-projects",
218 | "scan_count": 1
219 | },
220 | "wcolglazier/R42-2024": {
221 | "first_scan": "2025-10-16 01:46:51",
222 | "last_scan": "2025-10-16 01:46:51",
223 | "findings_count": 0,
224 | "scan_type": "auto:ai-projects",
225 | "scan_count": 1
226 | },
227 | "CitibankdemobusinessInc/Ok": {
228 | "first_scan": "2025-10-16 01:46:58",
229 | "last_scan": "2025-10-16 01:46:58",
230 | "findings_count": 0,
231 | "scan_type": "auto:ai-projects:failed",
232 | "scan_count": 1
233 | },
234 | "VaidehiAgarwal/SMARTREAD": {
235 | "first_scan": "2025-10-16 01:47:00",
236 | "last_scan": "2025-10-16 01:47:00",
237 | "findings_count": 0,
238 | "scan_type": "auto:ai-projects",
239 | "scan_count": 1
240 | },
241 | "DataStrategist/llm_knowledgegraph_from_pdf": {
242 | "first_scan": "2025-10-16 01:47:04",
243 | "last_scan": "2025-10-16 01:47:04",
244 | "findings_count": 0,
245 | "scan_type": "auto:ai-projects",
246 | "scan_count": 1
247 | },
248 | "mali1sav/embedding-visualisation": {
249 | "first_scan": "2025-10-16 01:47:08",
250 | "last_scan": "2025-10-16 01:47:08",
251 | "findings_count": 0,
252 | "scan_type": "auto:ai-projects",
253 | "scan_count": 1
254 | },
255 | "wanru122/midtern": {
256 | "first_scan": "2025-10-16 01:48:03",
257 | "last_scan": "2025-10-16 01:48:03",
258 | "findings_count": 1,
259 | "scan_type": "auto:ai-projects",
260 | "scan_count": 1
261 | },
262 | "abstractionlair/airproject": {
263 | "first_scan": "2025-10-16 01:48:08",
264 | "last_scan": "2025-10-16 01:48:08",
265 | "findings_count": 0,
266 | "scan_type": "auto:ai-projects",
267 | "scan_count": 1
268 | },
269 | "PhilipPfeffer/prototypeQA": {
270 | "first_scan": "2025-10-16 01:48:13",
271 | "last_scan": "2025-10-16 01:48:13",
272 | "findings_count": 0,
273 | "scan_type": "auto:ai-projects",
274 | "scan_count": 1
275 | },
276 | "AdelineXinyi/LLMChatbot": {
277 | "first_scan": "2025-10-16 01:48:17",
278 | "last_scan": "2025-10-16 01:48:17",
279 | "findings_count": 0,
280 | "scan_type": "auto:ai-projects",
281 | "scan_count": 1
282 | },
283 | "Maxtoshie/student-portal-platform": {
284 | "first_scan": "2025-10-16 01:48:51",
285 | "last_scan": "2025-10-16 01:48:51",
286 | "findings_count": 0,
287 | "scan_type": "auto:ai-projects",
288 | "scan_count": 1
289 | },
290 | "govindudusrihari/Nbn": {
291 | "first_scan": "2025-10-16 01:49:12",
292 | "last_scan": "2025-10-16 01:49:12",
293 | "findings_count": 0,
294 | "scan_type": "auto:ai-projects",
295 | "scan_count": 1
296 | },
297 | "parkbj12/sid": {
298 | "first_scan": "2025-10-16 01:49:15",
299 | "last_scan": "2025-10-16 01:49:15",
300 | "findings_count": 0,
301 | "scan_type": "auto:ai-projects:failed",
302 | "scan_count": 1
303 | },
304 | "alexanderher1324/ARproject---Flask": {
305 | "first_scan": "2025-10-16 01:49:39",
306 | "last_scan": "2025-10-16 01:49:39",
307 | "findings_count": 0,
308 | "scan_type": "auto:ai-projects",
309 | "scan_count": 1
310 | },
311 | "Benyapa-15/PA-5": {
312 | "first_scan": "2025-10-16 01:49:43",
313 | "last_scan": "2025-10-16 01:49:43",
314 | "findings_count": 0,
315 | "scan_type": "auto:ai-projects",
316 | "scan_count": 1
317 | },
318 | "tmrtrkr/CaseSpy": {
319 | "first_scan": "2025-10-16 01:49:56",
320 | "last_scan": "2025-10-16 01:49:56",
321 | "findings_count": 0,
322 | "scan_type": "auto:ai-projects",
323 | "scan_count": 1
324 | },
325 | "hwbjun/minipj": {
326 | "first_scan": "2025-10-16 01:49:58",
327 | "last_scan": "2025-10-16 01:49:58",
328 | "findings_count": 0,
329 | "scan_type": "auto:ai-projects",
330 | "scan_count": 1
331 | },
332 | "Nedzhin/weather_bot": {
333 | "first_scan": "2025-10-16 01:54:47",
334 | "last_scan": "2025-10-16 01:54:47",
335 | "findings_count": 0,
336 | "scan_type": "auto:ai-projects:failed",
337 | "scan_count": 1
338 | },
339 | "Elliotichi/placePal": {
340 | "first_scan": "2025-10-16 01:55:18",
341 | "last_scan": "2025-10-16 01:55:18",
342 | "findings_count": 0,
343 | "scan_type": "auto:ai-projects",
344 | "scan_count": 1
345 | },
346 | "SanyamGarg02/AI-Medical-Scribe": {
347 | "first_scan": "2025-10-16 01:55:25",
348 | "last_scan": "2025-10-16 01:55:25",
349 | "findings_count": 0,
350 | "scan_type": "auto:ai-projects",
351 | "scan_count": 1
352 | },
353 | "jayalath-jknr/pdf-streamlit": {
354 | "first_scan": "2025-10-16 03:07:17",
355 | "last_scan": "2025-10-16 03:07:17",
356 | "findings_count": 3,
357 | "scan_type": "auto:ai-projects",
358 | "scan_count": 1
359 | },
360 | "YASHWANTH1407/FactGuard": {
361 | "first_scan": "2025-10-16 03:07:46",
362 | "last_scan": "2025-10-16 03:07:46",
363 | "findings_count": 0,
364 | "scan_type": "auto:ai-projects",
365 | "scan_count": 1
366 | },
367 | "basilbowman/Cover_Letter_Generator_48hr_Project": {
368 | "first_scan": "2025-10-16 03:08:03",
369 | "last_scan": "2025-10-16 03:08:03",
370 | "findings_count": 0,
371 | "scan_type": "auto:ai-projects",
372 | "scan_count": 1
373 | },
374 | "harrison-e/llmsh": {
375 | "first_scan": "2025-10-16 03:08:08",
376 | "last_scan": "2025-10-16 03:08:08",
377 | "findings_count": 0,
378 | "scan_type": "auto:ai-projects",
379 | "scan_count": 1
380 | },
381 | "hongwon1031/MS_AI_project_2": {
382 | "first_scan": "2025-10-16 03:09:19",
383 | "last_scan": "2025-10-16 03:09:19",
384 | "findings_count": 0,
385 | "scan_type": "auto:ai-projects:failed",
386 | "scan_count": 1
387 | },
388 | "0kaiiix/gptyy": {
389 | "first_scan": "2025-10-16 03:09:22",
390 | "last_scan": "2025-10-16 03:09:22",
391 | "findings_count": 0,
392 | "scan_type": "auto:ai-projects",
393 | "scan_count": 1
394 | },
395 | "thyecust/OSTEP-QA": {
396 | "first_scan": "2025-10-16 03:10:21",
397 | "last_scan": "2025-10-16 03:10:21",
398 | "findings_count": 0,
399 | "scan_type": "auto:ai-projects",
400 | "scan_count": 1
401 | },
402 | "martianbandit/search_demo": {
403 | "first_scan": "2025-10-16 03:10:29",
404 | "last_scan": "2025-10-16 03:10:29",
405 | "findings_count": 0,
406 | "scan_type": "auto:ai-projects",
407 | "scan_count": 1
408 | },
409 | "sudo-atishay/AInsightHub": {
410 | "first_scan": "2025-10-16 03:10:40",
411 | "last_scan": "2025-10-16 03:10:40",
412 | "findings_count": 0,
413 | "scan_type": "auto:ai-projects",
414 | "scan_count": 1
415 | },
416 | "TwoAbove/lc": {
417 | "first_scan": "2025-10-16 03:10:44",
418 | "last_scan": "2025-10-16 03:10:44",
419 | "findings_count": 0,
420 | "scan_type": "auto:ai-projects",
421 | "scan_count": 1
422 | },
423 | "benlode/streamlit_chat_app": {
424 | "first_scan": "2025-10-16 03:10:48",
425 | "last_scan": "2025-10-16 03:10:48",
426 | "findings_count": 0,
427 | "scan_type": "auto:ai-projects",
428 | "scan_count": 1
429 | },
430 | "MeachSeaklav/M-and-E-Automate-System-user-input-prompt-": {
431 | "first_scan": "2025-10-16 03:10:57",
432 | "last_scan": "2025-10-16 03:10:57",
433 | "findings_count": 0,
434 | "scan_type": "auto:ai-projects",
435 | "scan_count": 1
436 | },
437 | "Ascensao/python_chatGPT_voice": {
438 | "first_scan": "2025-10-16 03:11:04",
439 | "last_scan": "2025-10-16 03:11:04",
440 | "findings_count": 2,
441 | "scan_type": "auto:ai-projects",
442 | "scan_count": 1
443 | },
444 | "24f1001940/project1-llm-based-automation-agent": {
445 | "first_scan": "2025-10-16 03:43:12",
446 | "last_scan": "2025-10-16 03:43:12",
447 | "findings_count": 0,
448 | "scan_type": "auto:ai-projects",
449 | "scan_count": 1
450 | },
451 | "joanix2/AppMeshCLI": {
452 | "first_scan": "2025-10-16 03:44:30",
453 | "last_scan": "2025-10-16 03:44:30",
454 | "findings_count": 0,
455 | "scan_type": "auto:ai-projects",
456 | "scan_count": 1
457 | },
458 | "Snow4DV/cli-tools": {
459 | "first_scan": "2025-10-16 03:44:40",
460 | "last_scan": "2025-10-16 03:44:40",
461 | "findings_count": 0,
462 | "scan_type": "auto:ai-projects",
463 | "scan_count": 1
464 | },
465 | "jocades/ai-translations": {
466 | "first_scan": "2025-10-16 03:44:49",
467 | "last_scan": "2025-10-16 03:44:49",
468 | "findings_count": 0,
469 | "scan_type": "auto:ai-projects",
470 | "scan_count": 1
471 | },
472 | "bocianowski1/marketeer": {
473 | "first_scan": "2025-10-16 03:45:04",
474 | "last_scan": "2025-10-16 03:45:04",
475 | "findings_count": 0,
476 | "scan_type": "auto:ai-projects",
477 | "scan_count": 1
478 | },
479 | "i-wanna-stay-at-your-house/update": {
480 | "first_scan": "2025-10-16 03:45:11",
481 | "last_scan": "2025-10-16 03:45:11",
482 | "findings_count": 0,
483 | "scan_type": "auto:ai-projects",
484 | "scan_count": 1
485 | },
486 | "kailasmanoj03/fiiinl": {
487 | "first_scan": "2025-10-16 03:45:13",
488 | "last_scan": "2025-10-16 03:45:13",
489 | "findings_count": 0,
490 | "scan_type": "auto:ai-projects",
491 | "scan_count": 1
492 | },
493 | "noreal001/bot": {
494 | "first_scan": "2025-10-16 03:45:34",
495 | "last_scan": "2025-10-16 03:45:34",
496 | "findings_count": 0,
497 | "scan_type": "auto:ai-projects",
498 | "scan_count": 1
499 | },
500 | "AdityaKanungo/OpenAI_Data_Reporting": {
501 | "first_scan": "2025-10-16 03:46:01",
502 | "last_scan": "2025-10-16 03:46:01",
503 | "findings_count": 1,
504 | "scan_type": "auto:ai-projects",
505 | "scan_count": 1
506 | },
507 | "Tej313/local_repo": {
508 | "first_scan": "2025-10-16 03:47:15",
509 | "last_scan": "2025-10-16 03:47:15",
510 | "findings_count": 0,
511 | "scan_type": "auto:ai-projects",
512 | "scan_count": 1
513 | },
514 | "tbrho/capstone": {
515 | "first_scan": "2025-10-16 03:47:39",
516 | "last_scan": "2025-10-16 03:47:39",
517 | "findings_count": 0,
518 | "scan_type": "auto:ai-projects:failed",
519 | "scan_count": 1
520 | },
521 | "nikhs-aim/CODES": {
522 | "first_scan": "2025-10-16 03:48:16",
523 | "last_scan": "2025-10-16 03:48:16",
524 | "findings_count": 0,
525 | "scan_type": "auto:ai-projects",
526 | "scan_count": 1
527 | },
528 | "linpz/chat-gpt-1": {
529 | "first_scan": "2025-10-16 03:48:32",
530 | "last_scan": "2025-10-16 03:48:32",
531 | "findings_count": 1,
532 | "scan_type": "auto:ai-projects",
533 | "scan_count": 1
534 | },
535 | "Sonjemin/-": {
536 | "first_scan": "2025-10-16 03:48:36",
537 | "last_scan": "2025-10-16 03:48:36",
538 | "findings_count": 0,
539 | "scan_type": "auto:ai-projects",
540 | "scan_count": 1
541 | },
542 | "ToolsLowcodeFlow/azure-openai-chat": {
543 | "first_scan": "2025-10-16 03:48:42",
544 | "last_scan": "2025-10-16 03:48:42",
545 | "findings_count": 0,
546 | "scan_type": "auto:ai-projects",
547 | "scan_count": 1
548 | },
549 | "Monear/introduction": {
550 | "first_scan": "2025-10-16 03:48:45",
551 | "last_scan": "2025-10-16 03:48:45",
552 | "findings_count": 0,
553 | "scan_type": "auto:ai-projects",
554 | "scan_count": 1
555 | },
556 | "ayusuf9/Gen-AI-Project": {
557 | "first_scan": "2025-10-16 03:48:51",
558 | "last_scan": "2025-10-16 03:48:51",
559 | "findings_count": 0,
560 | "scan_type": "auto:ai-projects",
561 | "scan_count": 1
562 | },
563 | "chanleeip/python_anywhere": {
564 | "first_scan": "2025-10-16 09:14:55",
565 | "last_scan": "2025-10-16 09:14:55",
566 | "findings_count": 2,
567 | "scan_type": "auto:ai-projects",
568 | "scan_count": 1
569 | },
570 | "sooryakumar9/gen_ai_progams": {
571 | "first_scan": "2025-10-16 09:15:07",
572 | "last_scan": "2025-10-16 09:15:07",
573 | "findings_count": 0,
574 | "scan_type": "auto:ai-projects",
575 | "scan_count": 1
576 | },
577 | "spak2005/twitter-ai-assistant": {
578 | "first_scan": "2025-10-16 09:15:24",
579 | "last_scan": "2025-10-16 09:15:24",
580 | "findings_count": 0,
581 | "scan_type": "auto:ai-projects",
582 | "scan_count": 1
583 | },
584 | "hwbjun/vectordb2": {
585 | "first_scan": "2025-10-16 09:15:29",
586 | "last_scan": "2025-10-16 09:15:29",
587 | "findings_count": 0,
588 | "scan_type": "auto:ai-projects",
589 | "scan_count": 1
590 | },
591 | "AmaanAnis/Building-Systems-with-the-ChatGPT-API-DL.AI": {
592 | "first_scan": "2025-10-16 09:15:38",
593 | "last_scan": "2025-10-16 09:15:38",
594 | "findings_count": 0,
595 | "scan_type": "auto:ai-projects",
596 | "scan_count": 1
597 | },
598 | "electricramblers/derivatives": {
599 | "first_scan": "2025-10-16 09:15:41",
600 | "last_scan": "2025-10-16 09:15:41",
601 | "findings_count": 0,
602 | "scan_type": "auto:ai-projects:failed",
603 | "scan_count": 1
604 | },
605 | "abdullahmamunn/python-voice-command": {
606 | "first_scan": "2025-10-16 09:15:55",
607 | "last_scan": "2025-10-16 09:15:55",
608 | "findings_count": 0,
609 | "scan_type": "auto:ai-projects:failed",
610 | "scan_count": 1
611 | },
612 | "imyourCxxmp/pa4_new": {
613 | "first_scan": "2025-10-16 09:15:59",
614 | "last_scan": "2025-10-16 09:15:59",
615 | "findings_count": 0,
616 | "scan_type": "auto:ai-projects",
617 | "scan_count": 1
618 | },
619 | "pangchewe/Co-Intelligence-Investor-Typhoon-Fassst": {
620 | "first_scan": "2025-10-16 09:16:03",
621 | "last_scan": "2025-10-16 09:16:03",
622 | "findings_count": 0,
623 | "scan_type": "auto:ai-projects",
624 | "scan_count": 1
625 | },
626 | "nitebyte/TripTeller": {
627 | "first_scan": "2025-10-16 09:16:08",
628 | "last_scan": "2025-10-16 09:16:08",
629 | "findings_count": 0,
630 | "scan_type": "auto:ai-projects",
631 | "scan_count": 1
632 | },
633 | "Niranjan5168/EnginSync-Placement-preparation-for-Engineering-Students": {
634 | "first_scan": "2025-10-16 09:31:32",
635 | "last_scan": "2025-10-16 09:31:32",
636 | "findings_count": 2,
637 | "scan_type": "auto:ai-projects",
638 | "scan_count": 1
639 | },
640 | "aebonlee/streamlit_team02": {
641 | "first_scan": "2025-10-16 09:32:17",
642 | "last_scan": "2025-10-16 09:32:17",
643 | "findings_count": 0,
644 | "scan_type": "auto:ai-projects",
645 | "scan_count": 1
646 | },
647 | "chienhenry/cv": {
648 | "first_scan": "2025-10-16 09:32:27",
649 | "last_scan": "2025-10-16 09:32:27",
650 | "findings_count": 0,
651 | "scan_type": "auto:ai-projects",
652 | "scan_count": 1
653 | },
654 | "vpxop111/pppp": {
655 | "first_scan": "2025-10-16 09:41:46",
656 | "last_scan": "2025-10-16 09:41:46",
657 | "findings_count": 0,
658 | "scan_type": "auto:ai-projects:failed",
659 | "scan_count": 1
660 | },
661 | "wsy258-strar/DocGPT": {
662 | "first_scan": "2025-10-16 09:42:09",
663 | "last_scan": "2025-10-16 09:42:09",
664 | "findings_count": 1,
665 | "scan_type": "auto:ai-projects",
666 | "scan_count": 1
667 | },
668 | "IncomeStreamSurfer/universal_scraping_script": {
669 | "first_scan": "2025-10-16 09:42:14",
670 | "last_scan": "2025-10-16 09:42:14",
671 | "findings_count": 0,
672 | "scan_type": "auto:ai-projects",
673 | "scan_count": 1
674 | },
675 | "xiaket/etc": {
676 | "first_scan": "2025-10-16 09:42:34",
677 | "last_scan": "2025-10-16 09:42:34",
678 | "findings_count": 0,
679 | "scan_type": "auto:ai-projects",
680 | "scan_count": 1
681 | },
682 | "harlev/eva-l": {
683 | "first_scan": "2025-10-16 09:42:34",
684 | "last_scan": "2025-10-16 09:42:34",
685 | "findings_count": 0,
686 | "scan_type": "auto:ai-projects:no-access",
687 | "scan_count": 1
688 | },
689 | "lukey625/ui": {
690 | "first_scan": "2025-10-16 09:42:35",
691 | "last_scan": "2025-10-16 09:42:35",
692 | "findings_count": 0,
693 | "scan_type": "auto:ai-projects:no-access",
694 | "scan_count": 1
695 | },
696 | "Benyapa-15/PA5-Benyapa-6540121822": {
697 | "first_scan": "2025-10-16 09:42:35",
698 | "last_scan": "2025-10-16 09:42:35",
699 | "findings_count": 0,
700 | "scan_type": "auto:ai-projects:no-access",
701 | "scan_count": 1
702 | },
703 | "perpendicooler/LLM-app-using-LangChain": {
704 | "first_scan": "2025-10-16 09:42:35",
705 | "last_scan": "2025-10-16 09:42:35",
706 | "findings_count": 0,
707 | "scan_type": "auto:ai-projects:no-access",
708 | "scan_count": 1
709 | },
710 | "q7950288/streamlit": {
711 | "first_scan": "2025-10-16 09:42:36",
712 | "last_scan": "2025-10-16 09:42:36",
713 | "findings_count": 0,
714 | "scan_type": "auto:ai-projects:no-access",
715 | "scan_count": 1
716 | },
717 | "rwill33/ai-document-analyzer": {
718 | "first_scan": "2025-10-16 09:42:36",
719 | "last_scan": "2025-10-16 09:42:36",
720 | "findings_count": 0,
721 | "scan_type": "auto:ai-projects:no-access",
722 | "scan_count": 1
723 | },
724 | "brihijoshi/generation_helpers": {
725 | "first_scan": "2025-10-16 09:42:36",
726 | "last_scan": "2025-10-16 09:42:36",
727 | "findings_count": 0,
728 | "scan_type": "auto:ai-projects:no-access",
729 | "scan_count": 1
730 | },
731 | "CristianoXico/mcp_br": {
732 | "first_scan": "2025-10-16 09:42:37",
733 | "last_scan": "2025-10-16 09:42:37",
734 | "findings_count": 0,
735 | "scan_type": "auto:ai-projects:no-access",
736 | "scan_count": 1
737 | },
738 | "Yale-LILY/NLP4Code": {
739 | "first_scan": "2025-10-16 09:42:37",
740 | "last_scan": "2025-10-16 09:42:37",
741 | "findings_count": 0,
742 | "scan_type": "auto:ai-projects:no-access",
743 | "scan_count": 1
744 | },
745 | "coleam00/Archon": {
746 | "first_scan": "2025-10-16 09:42:37",
747 | "last_scan": "2025-10-16 09:42:37",
748 | "findings_count": 0,
749 | "scan_type": "auto:ai-projects:no-access",
750 | "scan_count": 1
751 | },
752 | "kodigitaccount/AGENTIC_AI": {
753 | "first_scan": "2025-10-16 09:42:38",
754 | "last_scan": "2025-10-16 09:42:38",
755 | "findings_count": 0,
756 | "scan_type": "auto:ai-projects:no-access",
757 | "scan_count": 1
758 | },
759 | "hudsoncss/SFDCAudit": {
760 | "first_scan": "2025-10-16 09:42:38",
761 | "last_scan": "2025-10-16 09:42:38",
762 | "findings_count": 0,
763 | "scan_type": "auto:ai-projects:no-access",
764 | "scan_count": 1
765 | },
766 | "AIMLDev726/jaygoga_orchestra": {
767 | "first_scan": "2025-10-16 09:42:38",
768 | "last_scan": "2025-10-16 09:42:38",
769 | "findings_count": 0,
770 | "scan_type": "auto:ai-projects:no-access",
771 | "scan_count": 1
772 | },
773 | "uuu555552/ptocode": {
774 | "first_scan": "2025-10-16 09:42:38",
775 | "last_scan": "2025-10-16 09:42:38",
776 | "findings_count": 0,
777 | "scan_type": "auto:ai-projects:no-access",
778 | "scan_count": 1
779 | },
780 | "parthmaheshwari/emerging-sectors-forecast": {
781 | "first_scan": "2025-10-16 09:42:39",
782 | "last_scan": "2025-10-16 09:42:39",
783 | "findings_count": 0,
784 | "scan_type": "auto:ai-projects:no-access",
785 | "scan_count": 1
786 | },
787 | "ImprintLab/Medical-Graph-RAG": {
788 | "first_scan": "2025-10-16 09:42:39",
789 | "last_scan": "2025-10-16 09:42:39",
790 | "findings_count": 0,
791 | "scan_type": "auto:ai-projects:no-access",
792 | "scan_count": 1
793 | },
794 | "bxnlabs/litellm": {
795 | "first_scan": "2025-10-16 09:42:39",
796 | "last_scan": "2025-10-16 09:42:39",
797 | "findings_count": 0,
798 | "scan_type": "auto:ai-projects:no-access",
799 | "scan_count": 1
800 | },
801 | "adrshkr/mcp_server_openai": {
802 | "first_scan": "2025-10-16 09:42:40",
803 | "last_scan": "2025-10-16 09:42:40",
804 | "findings_count": 0,
805 | "scan_type": "auto:ai-projects:no-access",
806 | "scan_count": 1
807 | },
808 | "xcmt/ABC_report": {
809 | "first_scan": "2025-10-16 09:42:40",
810 | "last_scan": "2025-10-16 09:42:40",
811 | "findings_count": 0,
812 | "scan_type": "auto:ai-projects:no-access",
813 | "scan_count": 1
814 | },
815 | "justinnk/text-to-crn": {
816 | "first_scan": "2025-10-16 09:42:40",
817 | "last_scan": "2025-10-16 09:42:40",
818 | "findings_count": 0,
819 | "scan_type": "auto:ai-projects:no-access",
820 | "scan_count": 1
821 | },
822 | "hyChia88/MemoryCartography": {
823 | "first_scan": "2025-10-16 09:42:41",
824 | "last_scan": "2025-10-16 09:42:41",
825 | "findings_count": 0,
826 | "scan_type": "auto:ai-projects:no-access",
827 | "scan_count": 1
828 | },
829 | "AdityaMitt11/chatbot": {
830 | "first_scan": "2025-10-16 09:42:41",
831 | "last_scan": "2025-10-16 09:42:41",
832 | "findings_count": 0,
833 | "scan_type": "auto:ai-projects:no-access",
834 | "scan_count": 1
835 | },
836 | "RobHand27/AltCredit-HackIllinois-2025": {
837 | "first_scan": "2025-10-16 09:42:41",
838 | "last_scan": "2025-10-16 09:42:41",
839 | "findings_count": 0,
840 | "scan_type": "auto:ai-projects:no-access",
841 | "scan_count": 1
842 | },
843 | "SinchanaVenkatesh/TalktronAI": {
844 | "first_scan": "2025-10-16 09:42:42",
845 | "last_scan": "2025-10-16 09:42:42",
846 | "findings_count": 0,
847 | "scan_type": "auto:ai-projects:no-access",
848 | "scan_count": 1
849 | },
850 | "ASISys/Adrenaline": {
851 | "first_scan": "2025-10-16 09:42:42",
852 | "last_scan": "2025-10-16 09:42:42",
853 | "findings_count": 0,
854 | "scan_type": "auto:ai-projects:no-access",
855 | "scan_count": 1
856 | },
857 | "swarmauri/swarmauri-sdk": {
858 | "first_scan": "2025-10-16 09:42:42",
859 | "last_scan": "2025-10-16 09:42:42",
860 | "findings_count": 0,
861 | "scan_type": "auto:ai-projects:no-access",
862 | "scan_count": 1
863 | },
864 | "ivan-pua/pdf-rag-template": {
865 | "first_scan": "2025-10-16 09:42:43",
866 | "last_scan": "2025-10-16 09:42:43",
867 | "findings_count": 0,
868 | "scan_type": "auto:ai-projects:no-access",
869 | "scan_count": 1
870 | },
871 | "yiouyou/RePolyA": {
872 | "first_scan": "2025-10-16 09:42:43",
873 | "last_scan": "2025-10-16 09:42:43",
874 | "findings_count": 0,
875 | "scan_type": "auto:ai-projects:no-access",
876 | "scan_count": 1
877 | },
878 | "khaoss85/AI-Team-Orchestrator": {
879 | "first_scan": "2025-10-16 09:42:43",
880 | "last_scan": "2025-10-16 09:42:43",
881 | "findings_count": 0,
882 | "scan_type": "auto:ai-projects:no-access",
883 | "scan_count": 1
884 | },
885 | "renanmoretto/investidor-ia": {
886 | "first_scan": "2025-10-16 09:42:44",
887 | "last_scan": "2025-10-16 09:42:44",
888 | "findings_count": 0,
889 | "scan_type": "auto:ai-projects:no-access",
890 | "scan_count": 1
891 | },
892 | "SanJerry007/nonebot-plugin-chatgpt-api": {
893 | "first_scan": "2025-10-16 09:42:44",
894 | "last_scan": "2025-10-16 09:42:44",
895 | "findings_count": 0,
896 | "scan_type": "auto:ai-projects:no-access",
897 | "scan_count": 1
898 | },
899 | "Hubch/cad_recognition": {
900 | "first_scan": "2025-10-16 09:42:44",
901 | "last_scan": "2025-10-16 09:42:44",
902 | "findings_count": 0,
903 | "scan_type": "auto:ai-projects:no-access",
904 | "scan_count": 1
905 | },
906 | "rllm-org/rllm": {
907 | "first_scan": "2025-10-16 09:42:44",
908 | "last_scan": "2025-10-16 09:42:44",
909 | "findings_count": 0,
910 | "scan_type": "auto:ai-projects:no-access",
911 | "scan_count": 1
912 | },
913 | "usGuri-Dufutti/usGuri_Dufutti-backend": {
914 | "first_scan": "2025-10-17 03:06:20",
915 | "last_scan": "2025-10-17 03:06:20",
916 | "findings_count": 0,
917 | "scan_type": "auto:ai-projects:failed",
918 | "scan_count": 1
919 | },
920 | "Mikanwolfe/Maple": {
921 | "first_scan": "2025-10-17 03:06:30",
922 | "last_scan": "2025-10-17 03:06:30",
923 | "findings_count": 0,
924 | "scan_type": "auto:ai-projects",
925 | "scan_count": 1
926 | },
927 | "mcpflow/localmind": {
928 | "first_scan": "2025-10-17 03:08:32",
929 | "last_scan": "2025-10-17 03:08:32",
930 | "findings_count": 0,
931 | "scan_type": "auto:ai-projects",
932 | "scan_count": 1
933 | },
934 | "Sasmitha6821/Kalix-Chatbot": {
935 | "first_scan": "2025-10-17 03:08:38",
936 | "last_scan": "2025-10-17 03:08:38",
937 | "findings_count": 3,
938 | "scan_type": "auto:ai-projects",
939 | "scan_count": 1
940 | },
941 | "southriverai/srai-chat": {
942 | "first_scan": "2025-10-17 03:09:35",
943 | "last_scan": "2025-10-17 03:09:35",
944 | "findings_count": 0,
945 | "scan_type": "auto:ai-projects",
946 | "scan_count": 1
947 | },
948 | "AammarTufail/DSAAMP_2025": {
949 | "first_scan": "2025-10-17 03:12:12",
950 | "last_scan": "2025-10-17 03:12:12",
951 | "findings_count": 0,
952 | "scan_type": "auto:ai-projects:failed",
953 | "scan_count": 1
954 | },
955 | "hychaochao/Chat-Models-Backdoor-Attacking": {
956 | "first_scan": "2025-10-17 03:12:55",
957 | "last_scan": "2025-10-17 03:12:55",
958 | "findings_count": 0,
959 | "scan_type": "auto:ai-projects:failed",
960 | "scan_count": 1
961 | },
962 | "jtanaka104/openai_api_bot": {
963 | "first_scan": "2025-10-17 03:13:04",
964 | "last_scan": "2025-10-17 03:13:04",
965 | "findings_count": 0,
966 | "scan_type": "auto:ai-projects",
967 | "scan_count": 1
968 | },
969 | "sneezeparty/soupy": {
970 | "first_scan": "2025-10-17 03:13:22",
971 | "last_scan": "2025-10-17 03:13:22",
972 | "findings_count": 0,
973 | "scan_type": "auto:ai-projects",
974 | "scan_count": 1
975 | },
976 | "BitCodeHub/YappyAI": {
977 | "first_scan": "2025-10-17 03:17:59",
978 | "last_scan": "2025-10-17 03:17:59",
979 | "findings_count": 1,
980 | "scan_type": "auto:ai-projects",
981 | "scan_count": 1
982 | },
983 | "syedahirafatima/CloneChat.ai": {
984 | "first_scan": "2025-10-17 03:18:06",
985 | "last_scan": "2025-10-17 03:18:06",
986 | "findings_count": 0,
987 | "scan_type": "auto:ai-projects",
988 | "scan_count": 1
989 | },
990 | "KaranShah1/HW_Manager-HW2": {
991 | "first_scan": "2025-10-17 03:26:22",
992 | "last_scan": "2025-10-17 03:26:22",
993 | "findings_count": 0,
994 | "scan_type": "auto:ai-projects",
995 | "scan_count": 1
996 | },
997 | "TakanariShimbo/open-super-whisper-v2": {
998 | "first_scan": "2025-10-17 03:27:08",
999 | "last_scan": "2025-10-17 03:27:08",
1000 | "findings_count": 0,
1001 | "scan_type": "auto:ai-projects",
1002 | "scan_count": 1
1003 | },
1004 | "NNDSrinivas/mentor_app": {
1005 | "first_scan": "2025-10-17 03:27:08",
1006 | "last_scan": "2025-10-17 03:27:08",
1007 | "findings_count": 0,
1008 | "scan_type": "auto:ai-projects:no-access",
1009 | "scan_count": 1
1010 | },
1011 | "AlessioChen/traish": {
1012 | "first_scan": "2025-10-17 03:27:08",
1013 | "last_scan": "2025-10-17 03:27:08",
1014 | "findings_count": 0,
1015 | "scan_type": "auto:ai-projects:no-access",
1016 | "scan_count": 1
1017 | },
1018 | "mharish27/Hazard-Free-Navigation": {
1019 | "first_scan": "2025-10-17 03:27:09",
1020 | "last_scan": "2025-10-17 03:27:09",
1021 | "findings_count": 0,
1022 | "scan_type": "auto:ai-projects:no-access",
1023 | "scan_count": 1
1024 | },
1025 | "pj-13-shukla/serp-clustering-app": {
1026 | "first_scan": "2025-10-17 03:27:09",
1027 | "last_scan": "2025-10-17 03:27:09",
1028 | "findings_count": 0,
1029 | "scan_type": "auto:ai-projects:no-access",
1030 | "scan_count": 1
1031 | },
1032 | "sssmile2yong/linebot": {
1033 | "first_scan": "2025-10-17 03:27:10",
1034 | "last_scan": "2025-10-17 03:27:10",
1035 | "findings_count": 0,
1036 | "scan_type": "auto:ai-projects:no-access",
1037 | "scan_count": 1
1038 | },
1039 | "LachinFernando/speech_therapist": {
1040 | "first_scan": "2025-10-17 03:27:10",
1041 | "last_scan": "2025-10-17 03:27:10",
1042 | "findings_count": 0,
1043 | "scan_type": "auto:ai-projects:no-access",
1044 | "scan_count": 1
1045 | },
1046 | "HealthifyMe/coralogix-mcp": {
1047 | "first_scan": "2025-10-17 03:27:10",
1048 | "last_scan": "2025-10-17 03:27:10",
1049 | "findings_count": 0,
1050 | "scan_type": "auto:ai-projects:no-access",
1051 | "scan_count": 1
1052 | },
1053 | "BarryYin/AlphaTo": {
1054 | "first_scan": "2025-10-17 03:27:11",
1055 | "last_scan": "2025-10-17 03:27:11",
1056 | "findings_count": 0,
1057 | "scan_type": "auto:ai-projects:no-access",
1058 | "scan_count": 1
1059 | },
1060 | "surajshivkumar/aiba-rag": {
1061 | "first_scan": "2025-10-17 03:27:11",
1062 | "last_scan": "2025-10-17 03:27:11",
1063 | "findings_count": 0,
1064 | "scan_type": "auto:ai-projects:no-access",
1065 | "scan_count": 1
1066 | },
1067 | "cumbersomeamir/all_apis": {
1068 | "first_scan": "2025-10-17 03:27:11",
1069 | "last_scan": "2025-10-17 03:27:11",
1070 | "findings_count": 0,
1071 | "scan_type": "auto:ai-projects:no-access",
1072 | "scan_count": 1
1073 | },
1074 | "kmurray30/InterviewDictation": {
1075 | "first_scan": "2025-10-17 03:27:12",
1076 | "last_scan": "2025-10-17 03:27:12",
1077 | "findings_count": 0,
1078 | "scan_type": "auto:ai-projects:no-access",
1079 | "scan_count": 1
1080 | },
1081 | "HaotianZhai/EquivaFormulation": {
1082 | "first_scan": "2025-10-17 03:27:12",
1083 | "last_scan": "2025-10-17 03:27:12",
1084 | "findings_count": 0,
1085 | "scan_type": "auto:ai-projects:no-access",
1086 | "scan_count": 1
1087 | },
1088 | "BigDataIA-Fall2024-Team9/Assignment_1": {
1089 | "first_scan": "2025-10-17 03:27:13",
1090 | "last_scan": "2025-10-17 03:27:13",
1091 | "findings_count": 0,
1092 | "scan_type": "auto:ai-projects:no-access",
1093 | "scan_count": 1
1094 | },
1095 | "rsanmarting/phishinglab": {
1096 | "first_scan": "2025-10-17 03:27:13",
1097 | "last_scan": "2025-10-17 03:27:13",
1098 | "findings_count": 0,
1099 | "scan_type": "auto:ai-projects:no-access",
1100 | "scan_count": 1
1101 | },
1102 | "Debasish7ripathy/Fedora_AI_Logging": {
1103 | "first_scan": "2025-10-17 03:27:13",
1104 | "last_scan": "2025-10-17 03:27:13",
1105 | "findings_count": 0,
1106 | "scan_type": "auto:ai-projects:no-access",
1107 | "scan_count": 1
1108 | },
1109 | "mrzeznic/AI_devs_3": {
1110 | "first_scan": "2025-10-17 03:27:14",
1111 | "last_scan": "2025-10-17 03:27:14",
1112 | "findings_count": 0,
1113 | "scan_type": "auto:ai-projects:no-access",
1114 | "scan_count": 1
1115 | },
1116 | "0x7o/chatgpt-telegram-bot": {
1117 | "first_scan": "2025-10-17 03:27:14",
1118 | "last_scan": "2025-10-17 03:27:14",
1119 | "findings_count": 0,
1120 | "scan_type": "auto:ai-projects:no-access",
1121 | "scan_count": 1
1122 | },
1123 | "Ketan-K17/llama_cpp_experiments": {
1124 | "first_scan": "2025-10-17 03:27:14",
1125 | "last_scan": "2025-10-17 03:27:14",
1126 | "findings_count": 0,
1127 | "scan_type": "auto:ai-projects:no-access",
1128 | "scan_count": 1
1129 | },
1130 | "zwimpee/ea": {
1131 | "first_scan": "2025-10-17 03:27:15",
1132 | "last_scan": "2025-10-17 03:27:15",
1133 | "findings_count": 0,
1134 | "scan_type": "auto:ai-projects:no-access",
1135 | "scan_count": 1
1136 | },
1137 | "ThinkbrgAI/doctype": {
1138 | "first_scan": "2025-10-17 03:27:15",
1139 | "last_scan": "2025-10-17 03:27:15",
1140 | "findings_count": 0,
1141 | "scan_type": "auto:ai-projects:no-access",
1142 | "scan_count": 1
1143 | },
1144 | "shantanupatne/llm-robot": {
1145 | "first_scan": "2025-10-17 03:27:16",
1146 | "last_scan": "2025-10-17 03:27:16",
1147 | "findings_count": 0,
1148 | "scan_type": "auto:ai-projects:no-access",
1149 | "scan_count": 1
1150 | },
1151 | "thanhtw/PeerV6": {
1152 | "first_scan": "2025-10-17 03:27:16",
1153 | "last_scan": "2025-10-17 03:27:16",
1154 | "findings_count": 0,
1155 | "scan_type": "auto:ai-projects:no-access",
1156 | "scan_count": 1
1157 | },
1158 | "sreelakshmiarjun27/LLMCloudInfrastructureProject": {
1159 | "first_scan": "2025-10-17 03:27:16",
1160 | "last_scan": "2025-10-17 03:27:16",
1161 | "findings_count": 0,
1162 | "scan_type": "auto:ai-projects:no-access",
1163 | "scan_count": 1
1164 | },
1165 | "JapiKredi/crewAI-basics": {
1166 | "first_scan": "2025-10-17 03:27:17",
1167 | "last_scan": "2025-10-17 03:27:17",
1168 | "findings_count": 0,
1169 | "scan_type": "auto:ai-projects:no-access",
1170 | "scan_count": 1
1171 | },
1172 | "michalzagrodzki/compliance-tracker-mvp-api": {
1173 | "first_scan": "2025-10-17 03:27:17",
1174 | "last_scan": "2025-10-17 03:27:17",
1175 | "findings_count": 0,
1176 | "scan_type": "auto:ai-projects:no-access",
1177 | "scan_count": 1
1178 | },
1179 | "vespa-engine/sample-apps": {
1180 | "first_scan": "2025-10-17 03:27:18",
1181 | "last_scan": "2025-10-17 03:27:18",
1182 | "findings_count": 0,
1183 | "scan_type": "auto:ai-projects:no-access",
1184 | "scan_count": 1
1185 | },
1186 | "Njanja2025/Baddy-Agent": {
1187 | "first_scan": "2025-10-17 03:27:18",
1188 | "last_scan": "2025-10-17 03:27:18",
1189 | "findings_count": 0,
1190 | "scan_type": "auto:ai-projects:no-access",
1191 | "scan_count": 1
1192 | },
1193 | "sindhu-satish/sachacks_vi": {
1194 | "first_scan": "2025-10-17 03:27:18",
1195 | "last_scan": "2025-10-17 03:27:18",
1196 | "findings_count": 0,
1197 | "scan_type": "auto:ai-projects:no-access",
1198 | "scan_count": 1
1199 | },
1200 | "NoamMichael/Comparing-Confidence-in-LLMs": {
1201 | "first_scan": "2025-10-17 03:27:19",
1202 | "last_scan": "2025-10-17 03:27:19",
1203 | "findings_count": 0,
1204 | "scan_type": "auto:ai-projects:no-access",
1205 | "scan_count": 1
1206 | },
1207 | "jwm4/llama-stack-odh": {
1208 | "first_scan": "2025-10-17 03:27:19",
1209 | "last_scan": "2025-10-17 03:27:19",
1210 | "findings_count": 0,
1211 | "scan_type": "auto:ai-projects:no-access",
1212 | "scan_count": 1
1213 | },
1214 | "HappyPathway/musical-rental-inventory-django": {
1215 | "first_scan": "2025-10-17 03:27:19",
1216 | "last_scan": "2025-10-17 03:27:19",
1217 | "findings_count": 0,
1218 | "scan_type": "auto:ai-projects:no-access",
1219 | "scan_count": 1
1220 | },
1221 | "srimanthds/cochlear3-qabot": {
1222 | "first_scan": "2025-10-17 03:27:20",
1223 | "last_scan": "2025-10-17 03:27:20",
1224 | "findings_count": 0,
1225 | "scan_type": "auto:ai-projects:no-access",
1226 | "scan_count": 1
1227 | },
1228 | "factly/gopie": {
1229 | "first_scan": "2025-10-17 03:27:20",
1230 | "last_scan": "2025-10-17 03:27:20",
1231 | "findings_count": 0,
1232 | "scan_type": "auto:ai-projects:no-access",
1233 | "scan_count": 1
1234 | },
1235 | "HGF-hgf/RAG": {
1236 | "first_scan": "2025-10-17 03:27:21",
1237 | "last_scan": "2025-10-17 03:27:21",
1238 | "findings_count": 0,
1239 | "scan_type": "auto:ai-projects:no-access",
1240 | "scan_count": 1
1241 | },
1242 | "phanideva/gen_ai_financial_report_generator": {
1243 | "first_scan": "2025-10-17 03:27:21",
1244 | "last_scan": "2025-10-17 03:27:21",
1245 | "findings_count": 0,
1246 | "scan_type": "auto:ai-projects:no-access",
1247 | "scan_count": 1
1248 | },
1249 | "SMOG-Devs/jargone": {
1250 | "first_scan": "2025-10-17 03:27:21",
1251 | "last_scan": "2025-10-17 03:27:21",
1252 | "findings_count": 0,
1253 | "scan_type": "auto:ai-projects:no-access",
1254 | "scan_count": 1
1255 | },
1256 | "Rperry2174/faster_crowssword_generator": {
1257 | "first_scan": "2025-10-17 03:27:22",
1258 | "last_scan": "2025-10-17 03:27:22",
1259 | "findings_count": 0,
1260 | "scan_type": "auto:ai-projects:no-access",
1261 | "scan_count": 1
1262 | }
1263 | },
1264 | "total_scanned": 180,
1265 | "last_updated": "2025-10-17 03:27:22"
1266 | }
--------------------------------------------------------------------------------