├── 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 | [![Python](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/) 8 | [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) 9 | [![GitHub Actions](https://img.shields.io/badge/automation-GitHub%20Actions-2088FF.svg)](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 | } --------------------------------------------------------------------------------