├── pattern
├── info.db
└── file.db
├── Config.py
├── LICENSE
├── ColorPrint.py
├── README.md
├── .gitignore
├── static
├── report.html
└── jquery.min.js
├── README_GitPrey.md
└── GitLeak.py
/pattern/info.db:
--------------------------------------------------------------------------------
1 | email
2 | password
3 | passwd
4 | pwd
5 | secret
--------------------------------------------------------------------------------
/pattern/file.db:
--------------------------------------------------------------------------------
1 | config
2 | credential
3 | properties
4 | backup
5 | dump
6 | password
7 | secret
8 | setting
9 | env
10 | conf
11 | mail
12 | conn
13 | ini
14 | const
--------------------------------------------------------------------------------
/Config.py:
--------------------------------------------------------------------------------
1 | # Relative path of pattern file to GitPrey
2 | FILE_DB = "pattern/file.db"
3 | INFO_DB = "pattern/info.db"
4 |
5 | # GitHub account config for searching
6 | USER_NAME = ""
7 | PASSWORD = ""
8 |
9 | # Blacklist
10 | EXT_BLACKLIST = [".ico", ".flv", ".css", ".jpg", ".png", ".jpeg", ".gif", ".pdf", ".ss3", ".rar", ".zip", ".avi", ".mp4", ".swf", ".wmi", ".exe", ".mpeg", ".dll", ".pcap", ".log", ".class", ".html"]
11 | LANG_BLACKLIST = ["html", "jsp", "smali"]
12 | LINE_MUSTHAVE = ['=', ':', 'define']
13 | REPO_NAME_BLACKLIST = ['spider', 'crawl']
14 |
15 | # other
16 | MAX_INFONUM = 3
17 | MAX_LINELEN = 512
18 | MAX_COUNT_SINGLE_FILE = 20
19 | MAX_SEARCH_REPO = 20
20 | MAX_REPO_SINGLE_SEARCH = 5
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 5alt
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 |
--------------------------------------------------------------------------------
/ColorPrint.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding = utf-8
3 |
4 | try:
5 | from colorama import init, Fore
6 | except ImportError:
7 | print "[!_!]ERROR INFO: You have to install colorama module."
8 | exit()
9 |
10 | import logging
11 |
12 | init(autoreset=True)
13 |
14 | logger = logging.getLogger('')
15 | logger.setLevel(logging.INFO)
16 | file_handle = logging.FileHandler('GitLeak.log')
17 | file_handle.setLevel(logging.INFO)
18 | formatter = logging.Formatter('%(message)s')
19 | file_handle.setFormatter(formatter)
20 | logger.addHandler(file_handle)
21 |
22 |
23 | def error_print(string):
24 | # Print error information with red color
25 | print Fore.RED + string
26 | logger.error(string)
27 |
28 |
29 | def info_print(string):
30 | # Print information with green color
31 | print Fore.GREEN + string
32 | logger.info(string)
33 |
34 |
35 | def project_print(string):
36 | # Print project information with deep green color
37 | print Fore.CYAN + string
38 | logger.info(string)
39 |
40 |
41 | def file_print(string):
42 | # Print file url with yellow color
43 | print Fore.YELLOW + string
44 | logger.info(string)
45 |
46 |
47 | def code_print(string):
48 | # Print code line with white color
49 | print Fore.WHITE + string
50 | logger.info(string)
51 |
52 |
53 | if __name__ == "__main__":
54 | pass
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitLeak
2 |
3 | GitLeak 是一个从 Github 上查找密码信息的小工具。在[GitPrey](https://github.com/repoog/GitPrey)的基础上修改而来。攻击者在 Github 上搜索主要目的是获取一些密码,GitLeak 针对这个痛点进行了优化搜索。
4 |
5 | ### 程序使用帮助
6 | ```
7 | USAGE:
8 | -l Set level for searching within 1~5, default level is 1.
9 | -k Set key words for searching projects.
10 | -h Show help information.
11 | ```
12 | * -l:选填参数,用于设置代码搜索深度;
13 | * -k:必填参数,用于设置搜索关键词,若关键词中包含空白字符,需用双引号将关键词括起来;
14 | * -h:帮助信息。
15 |
16 | ### 文件配置说明
17 | pattern为搜索项文件配置目录,相关文件说明如下:
18 | * file.db:敏感内容关键词搜索的文件名称范围,内容搜索在该文件名称范围内进行,如:.env
19 | * info.db:敏感内容关键词(由于AND/OR/NOT操作符在Github单次搜索中最多支持五个,故关键词会进行轮询),如:password
20 |
21 | `Config.py`为用户配置文件,需要在里面指定 Github 的账号和密码
22 | * EXT_BLACKLIST: 搜索文件内容时后缀名黑名单
23 | * LANG_BLACKLIST:搜索 repo 时语言的黑名单
24 | * MAX_INFONUM:一次最多用于搜索的关键词数量
25 | * MAX_LINELEN:搜索内容时一行最大的长度限制
26 | * MAX_COUNT_SINGLE_FILE:一个文件最多包含关键词的次数
27 | * MAX_SEARCH_REPO:最多搜索的 repo 数
28 | * MAX_REPO_SINGLE_SEARCH:每次搜索用的最多 repo 数量
29 |
30 | ## 工作流程
31 | 1. 根据用户的关键字获取 repo 列表
32 |
33 | GitLeak 会把用户输入的关键字以空格分隔并加上双引号,按照best match方式排序,在搜索结果中去除黑名单里的语言种类,在翻页搜索时去除掉已获得的 repo,最多保留20个repo(可以配置)。
34 |
35 | 2. 在 repo 的敏感文件里搜索敏感信息
36 |
37 | GitLeak 会通过`repo`关键字和`filename`关键字指定搜索范围进行关键字搜索,并对文件的后缀进行判断过滤,对文件内容出现敏感信息的行打印出来。该行必须包含敏感信息并且有赋值的操作(存在`:`或者`=`)才会被记录。
38 |
39 | 3. 输出报告,方便查看
40 |
41 | ## License
42 | MIT
43 |
44 | ## 联系
45 | http://5alt.me
46 |
47 | md5_salt [AT] qq.com
48 |
49 | ## 参考
50 |
51 | https://github.com/repoog/GitPrey
52 |
53 | https://help.github.com/articles/about-searching-on-github/
54 |
55 | https://help.github.com/articles/understanding-the-search-syntax/
56 |
57 | https://help.github.com/articles/searching-code/
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | .idea/
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | .hypothesis/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # dotenv
85 | .env
86 |
87 | # virtualenv
88 | .venv
89 | venv/
90 | ENV/
91 |
92 | # Spyder project settings
93 | .spyderproject
94 | .spyproject
95 |
96 | # Rope project settings
97 | .ropeproject
98 |
99 | # mkdocs documentation
100 | /site
101 |
102 | # mypy
103 | .mypy_cache/
104 |
--------------------------------------------------------------------------------
/static/report.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
80 |
81 |
--------------------------------------------------------------------------------
/README_GitPrey.md:
--------------------------------------------------------------------------------
1 | ## GitHub敏感信息扫描工具
2 | ### 功能设计说明
3 | GitPrey是根据企业关键词进行项目检索以及相应敏感文件和敏感文件内容扫描的工具,其设计思路如下:
4 | * 根据关键词在GitHub中进行全局代码内容和路径的搜索(in:file,path),将项目结果做项目信息去重整理得到所有关键词相关的项目,即疑似项目结果;
5 | * 基于PATTERN_DB中的敏感文件名或敏感代码对所有疑似项目做文件名搜索(filename:)和代码搜索(in:file);
6 | * 将匹配搜索到的结果按照项目整理输出;
7 | 由于无法做到精确匹配和精确识别,因此扫描结果或存在一定的漏报(比如项目中未出现关键词路径或内容)或误报(比如第三方项目引用关键词内容)情况,其中漏报的原因还包括Github的搜索限制:
8 | * 默认只搜索主分支代码,多数情况下是master分支;
9 | * Github最大只允许搜索1000条代码项,即100页代码;
10 | * 代码搜索仅搜索不大于384Kb的文件;
11 |
12 | 此外,不同关键词搜索的疑似项目数量不同,少则数个,多则数十个甚至数百个,并会对搜索和扫描时间造成直接影响(另一影响因素是匹配的文件名关键词数量和内容关键词数量),项目和关键词越多,扫描时间越长。因此可以根据需要进行扫描深度的选择,这一维度由GitHub最近索引(Recently Indexed)排序的代码页决定,深度越深,检索的项目数量越多,反之亦然。深度选项和说明如下:
13 | * Level 1:检索最近索引的前10页代码页;
14 | * Level 2:检索最近索引的前20页代码页;
15 | * Level 3:检索最近索引的前50页代码页;
16 | * Level 4:检索最近索引的前70页代码页;
17 | * Level 5:检索最近索引的前100页代码页;
18 |
19 | 深度选择与企业扫描周期性应该成正相关,深度选择小,则相应扫描的周期性也应当较小,如深度选择为Level 1,则相应的扫描周期基于企业情况可定为每天或每周,深度选择为Level 5,则相应的扫描周期可适当延长。例如,关键词“Google”最大(Level 5)可搜索两天前上传的项目代码,而关键词“repoog”搜索结果则不足1页。
20 |
21 | ### 技术实现说明
22 | 项目配置文件Config.py中需要配置使用者的Github用户名、密码:
23 | * 未登录Github进行代码搜索会因为请求速度过快(约10页代码结果页)而返回HTTP STATUE 429,即Too Many Requests的错误,因此需要登录后进行搜索;
24 | * 在项目内关键词文件名和关键词内容扫描时未采用API,原因有两点:一是搜索代码的API频率限制很大(认证后30次/分钟)无法满足快速搜索;二是某些项目关键词的搜索结果项超过100条,而API在设置per_page参数后至多支持展现100条结果项;
25 | 项目配置文件Config.py中需要配置FILE_DB/INFO_DB/PASS_DB/PATH_DB项,用途如下:_
26 | * 敏感文件搜索是基于配置项中的PATH_DB内容检索特定文件的泄漏;
27 | * 敏感内容搜索是基于PASS_DB和FILE_DB进行检索,再根据INFO_DB和PASS_DB输出相关代码行;_
28 |
29 | ### 程序使用帮助
30 | GitPrey v2.2版本后去除了ACCESS_TOKEN的配置以及配置文件中的SEARCH_LEVEL和KEYWORDS配置项,改用命令行参数方式执行:
31 | ```
32 | USAGE:
33 | -l Set level for searching within 1~5, default level is 1.
34 | -k Set key words for searching projects.
35 | -h Show help information.
36 | ```
37 | * -l:选填参数,用于设置代码搜索深度;
38 | * -k:必填参数,用于设置搜索关键词,若关键词中包含空白字符,需用双引号将关键词括起来;
39 | * -h:帮助信息。
40 |
41 | ### 文件配置说明
42 | pattern为搜索项文件配置目录,相关文件说明如下:
43 | * path.db:敏感文件名或文件后缀,用于搜索文件名,如:htpasswd
44 | * file.db:敏感内容关键词搜索的文件名称范围,内容搜索在该文件名称范围内进行,如:.env
45 | * info.db:敏感内容关键词(由于AND/OR/NOT操作符在Github单次搜索中最多支持五个,故关键词会进行轮询),如:password
46 |
47 | ### 程序更新列表
48 | * v1.0 初始版本
49 | * v2.0 更新搜索设计和算法
50 | * v2.1 更新搜索结果输出展现
51 | * v2.2 优化部分代码,增加项目搜索进度条,解决代码输出BUG
52 | * v2.4 优化程序目录设计,优化源码实现,增加默认文件输出
53 | * v2.5 优化代码搜索为整页代码搜索,优化颜色输出及文件输出,优化代码实现
54 |
55 | ***
56 | ## Sensitive info scan tool of Github
57 | ### Function introduction and design
58 | GitPrey is a tool for searching sensitive information or data according to company name or key word something.The design mind is from searching sensitive data leakling in Github:
59 | * Search code in file and path according to key word to get all related projects;
60 | * Search code in every related project to find matching file or content in PATTERN_DB;
61 | * Output all matching file information,project information and user information;
62 |
63 | By the way, there is some missing file or mistake file with using Gitprey,the reason is:
64 | * Only the default branch is considered by Github. In most cases, this will be the master branch.
65 | * Only files smaller than 384 KB are searchable by Github.
66 | * Github only make up to 1,000 results for each search.
67 |
68 | Gitprey also provides the search level to adjust scanning deep, it's between Level 1 to Level 5:
69 | * Level 1: Only search 10 pages in recently indexed code results.
70 | * Level 2: Only search 20 pages in recently indexed code results.
71 | * Level 3: Only search 50 pages in recently indexed code results.
72 | * Level 4: Only search 70 pages in recently indexed code results.
73 | * Level 5: Only search 100 pages in recently indexed code results.
74 |
75 | You can modify the Level in Config.py.To search as quick as you can,you must configure your own Github account username and password to avoid 429 ERROR which is too many requests.
76 |
77 | ### Tech detail introduction
78 | There are some hints to declare about technological details:
79 | * Github API is not used in searching code,because its rate limit up to 30 times per minute,even if you authenticate by access token.
80 | * Only user information crawler used Github API,it's enough for scanning speed.
81 | You have to config FILE_DB/INFO_DB/PASS_DB/PATH_DB in config.py:
82 | * PATH_DB is used to search specific file in related projects when searching file leaking.
83 | * FILE_DB and PASS_DB are used to searching sensitive content in related projects when searching content leaking, while INFO_DB and PASS_DB is used to output code line._
84 |
85 | ### GitPrey usage
86 | GitPrey removed ACCESS_TOKEN, SEARCH_LEVEL and KEYWORDS configuration from v2.2:
87 | USAGE:
88 | -l Set search level for searching projects within 1-5, default level is 1.
89 | -k Set key words for searching projects.
90 | -h Show help information.
91 |
92 | ### pattern file introduction
93 | pattern is a directory putting db files:
94 | * path.db: sensitive file path, such as htpasswd
95 | * file.db: the files name which sensitive content maybe in, such as .env
96 | * info.db: sensitive content key words for searching, such as password
97 |
98 |
--------------------------------------------------------------------------------
/GitLeak.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | try:
5 | import requests
6 | from requests.auth import HTTPBasicAuth
7 | except ImportError:
8 | print "[!_!]ERROR INFO: You have to install requests module."
9 | exit()
10 |
11 | try:
12 | from bs4 import BeautifulSoup
13 | except ImportError:
14 | print "[!_!]ERROR INFO: You have to install BeautifulSoup module."
15 | exit()
16 |
17 | import re
18 | import sys
19 | import time
20 | import imp
21 | import argparse
22 | import os
23 |
24 | from urllib import quote
25 |
26 | import json
27 |
28 | try:
29 | from Config import *
30 | except ImportError:
31 | print "[!_!]ERROR INFO: Can't find Config file for searching."
32 | exit()
33 |
34 | try:
35 | from ColorPrint import *
36 | except ImportError:
37 | print "[!_!]ERROR INFO: Can't find ColorPrint file for printing."
38 | exit()
39 |
40 | HOST_NAME = "https://github.com/"
41 | RAW_NAME = "https://raw.githubusercontent.com/"
42 | SCAN_DEEP = [10, 30, 50, 70, 100] # Scanning deep according to page searching count and time out seconds
43 | SEARCH_LEVEL = 1 # Code searching level within 1-5, default is 1
44 | MAX_PAGE_NUM = 100 # Maximum results of code searching
45 | MAX_RLT_PER_PAGE = 10 # Maximum results count of per page
46 |
47 |
48 | class GitPrey(object):
49 | """
50 | _______ __ __ __
51 | / ____(_) /_/ / ___ ____ _/ /__
52 | / / __/ / __/ / / _ \/ __ `/ //_/
53 | / /_/ / / /_/ /___/ __/ /_/ / ,<
54 | \____/_/\__/_____/\___/\__,_/_/|_|
55 |
56 |
57 | Author: md5_salt
58 | Special thanks to Cooper Pei
59 | """
60 |
61 | def __init__(self, keyword):
62 | self.keyword = ' '.join(['"%s"'%i for i in keyword.split(' ')])
63 | self.search_url = "https://github.com/search?o=desc&p={page}&q={keyword}&ref=searchresults&s=&type=Code&utf8=%E2%9C%93"
64 | self.headers = {
65 | 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36"}
66 | self.cookies = ""
67 |
68 | def search_project(self):
69 | """
70 | Search related projects with recently indexed sort according to keyword
71 | :returns: Related projects list
72 | """
73 | unique_project_list = []
74 | self.__auto_login(USER_NAME, PASSWORD)
75 | info_print('[@_@] Searching projects hard...')
76 |
77 | # Get unique project list of first page searched results
78 | total_progress = SCAN_DEEP[SEARCH_LEVEL - 1]
79 | query_string = self.keyword + " -language:" + " -language:".join(LANG_BLACKLIST)
80 | for i in xrange(total_progress):
81 | # Print process of searching project
82 | progress_point = int((i + 1) * (100 / total_progress))
83 | sys.stdout.write(str(progress_point) + '%|' + '#' * progress_point + '|\r')
84 | sys.stdout.flush()
85 | # Search project in each page
86 | code_url = self.search_url.format(page=1, keyword=quote(query_string))
87 | page_html_parse = self.__get_page_html(code_url)
88 | project_list = self.__page_project_list(page_html_parse) # Project list of per result page
89 | page_project_num, project_list = len(project_list), list(set(project_list))
90 | unique_project_list.extend(project_list) # Extend unique project list of per page
91 | if page_project_num < MAX_RLT_PER_PAGE:
92 | break
93 | project = " -repo:" + " -repo:".join(project_list)
94 | query_string += project
95 | # Deal with last progress bar stdout
96 | sys.stdout.write('100%|' + '#' * 100 + '|\r')
97 | sys.stdout.flush()
98 | return unique_project_list
99 |
100 | @staticmethod
101 | def __page_project_list(page_html):
102 | """
103 | Get project list of one searching result page
104 | :param page_html: Html page content
105 | :returns: Project list of per page
106 | """
107 | cur_par_html = BeautifulSoup(page_html, "lxml")
108 | project_info = cur_par_html.select("a.text-bold")
109 | page_project = [project.text for project in project_info if not project.text.endswith('.github.io') and not len([i for i in REPO_NAME_BLACKLIST if i in project.text.lower()])]
110 | return page_project
111 |
112 | def sensitive_info_query(self, project_string):
113 | """
114 | Search sensitive information and sensitive file from projects
115 | :param project_string: Key words string for querying
116 | :returns: None
117 | """
118 | # Output code line with sensitive key words like username.
119 | info_sig_list = self.__pattern_db_list(INFO_DB)
120 | file_sig_list = self.__pattern_db_list(FILE_DB)
121 | file_pattern = " filename:" + " filename:".join(file_sig_list)
122 | code_dic = {}
123 | # Most five AND/OR operators in search function.
124 | for i in xrange(len(info_sig_list)/MAX_INFONUM+1):
125 | project_pattern = info_sig_list[i*MAX_INFONUM:i*MAX_INFONUM+MAX_INFONUM]
126 |
127 | if len(project_pattern) == 0: break
128 |
129 | repo_code_dic = self.__file_content_inspect(project_string, file_pattern, project_pattern)
130 | code_dic.update(repo_code_dic)
131 | return code_dic
132 |
133 | def __file_content_inspect(self, project_string, file_pattern, project_pattern):
134 | """
135 | Check sensitive code in particular project
136 | :param content_query_string: Content string for searching
137 | :param info_sig_match: information signature match regular
138 | :returns: None
139 | """
140 | if not project_pattern: return {}
141 | query_string = " OR ".join(project_pattern)
142 | repo_file_dic = self.__file_name_inspect(query_string + project_string + file_pattern)
143 | repo_code_dic = {}
144 | for repo_name in repo_file_dic:
145 | self.__output_project_info(repo_name)
146 | repo_code_dic[repo_name] = {} # Set code line dictionary
147 | for file_url in repo_file_dic[repo_name]:
148 | file_url_output = "[-] Compromise File: {file_url}"
149 | file_print(file_url_output.format(file_url=file_url))
150 | repo_code_dic[repo_name][file_url] = [] # Set code block of project file
151 | # Read codes from raw file by replace host to raw host.
152 | code_file = self.__get_page_html(file_url.replace(HOST_NAME, RAW_NAME).replace('blob/', ''))
153 | for code_line in code_file.split('\n'):
154 | if len(repo_code_dic[repo_name][file_url]) > MAX_COUNT_SINGLE_FILE: break
155 | line_check = [i for i in LINE_MUSTHAVE if i in code_line]
156 | if not line_check: continue
157 | account_code = re.search('|'.join(project_pattern), code_line, re.I)
158 | if account_code:
159 | code = code_line.encode('utf-8').strip()
160 | if len(code) > MAX_LINELEN: continue
161 | code_print(code)
162 | repo_code_dic[repo_name][file_url].append(code)
163 | else:
164 | continue
165 | if len(repo_code_dic[repo_name][file_url]) > MAX_COUNT_SINGLE_FILE or not repo_code_dic[repo_name][file_url]:
166 | del repo_code_dic[repo_name][file_url]
167 |
168 | return repo_code_dic
169 |
170 | def __file_name_inspect(self, file_query_string, print_mode=0):
171 | """
172 | Inspect sensitive file in particular project
173 | :param file_query_string: File string for searching
174 | :returns: None
175 | """
176 | page_num = 1
177 | repo_file_dic = {}
178 | while page_num <= SCAN_DEEP[SEARCH_LEVEL - 1]:
179 | check_url = self.search_url.format(page=page_num, keyword=quote(file_query_string))
180 | page_html = self.__get_page_html(check_url)
181 | project_html = BeautifulSoup(page_html, 'lxml')
182 | repo_list = project_html.select('div .d-inline-block.col-10 > a:nth-of-type(2)')
183 | if not repo_list:
184 | break
185 | # Handle file links for each project
186 | for repo in repo_list:
187 | file_url = repo.attrs['href']
188 | cur_project_name = "/".join(file_url.split("/")[1:3])
189 | if cur_project_name not in repo_file_dic.keys():
190 | if print_mode:
191 | self.__output_project_info(cur_project_name)
192 | file_print("[-] Compromise File:")
193 | repo_file_dic[cur_project_name] = [] # Set compromise project item
194 | if os.path.splitext(file_url)[1].lower() not in EXT_BLACKLIST:
195 | repo_file_dic[cur_project_name].append(HOST_NAME + file_url[1:]) # Set compromise project file item
196 | if print_mode:
197 | file_print(HOST_NAME + file_url[1:])
198 | page_num += 1
199 |
200 | return repo_file_dic
201 |
202 | @staticmethod
203 | def __pattern_db_list(file_path):
204 | """
205 | Read file name pattern item from signature file
206 | :param file_path: Pattern file path
207 | :returns: Signature item list
208 | """
209 | item_list = []
210 | with open(file_path, 'r') as pattern_file:
211 | item_line = pattern_file.readline()
212 | while item_line:
213 | item_list.append(item_line.strip())
214 | item_line = pattern_file.readline()
215 | return item_list
216 |
217 | @staticmethod
218 | def __output_project_info(project):
219 | """
220 | Output user information and project information of particular project
221 | :returns: None
222 | """
223 | user_name, project_name = project.split(r"/")
224 | user_info = "[+_+] User Nickname: {nickname}"
225 | project_print(user_info.format(nickname=user_name))
226 | project_info = "[+_+] Project Name: {name}"
227 | project_print(project_info.format(name=project_name))
228 | project_info = "[+_+] Project Link: {link}"
229 | project_print(project_info.format(link=HOST_NAME + project))
230 |
231 | def __auto_login(self, username, password):
232 | """
233 | Get cookie for logining GitHub
234 | :returns: None
235 | """
236 | login_request = requests.Session()
237 | login_html = login_request.get("https://github.com/login", headers=self.headers)
238 | post_data = {}
239 | soup = BeautifulSoup(login_html.text, "lxml")
240 | input_items = soup.find_all('input')
241 | for item in input_items:
242 | post_data[item.get('name')] = item.get('value')
243 | post_data['login'], post_data['password'] = username, password
244 | login_request.post("https://github.com/session", data=post_data, headers=self.headers)
245 | self.cookies = login_request.cookies
246 | if self.cookies['logged_in'] == 'no':
247 | error_print('[!_!] ERROR INFO: Login Github failed, please check account in config file.')
248 | exit()
249 |
250 | def __get_page_html(self, url):
251 | """
252 | Get parse html page from requesting url
253 | :param url: Requesting url
254 | :returns: Parsed html page
255 | """
256 | try:
257 | page_html = requests.get(url, headers=self.headers, cookies=self.cookies, timeout=SCAN_DEEP[SEARCH_LEVEL - 1])
258 | if page_html.status_code == 429:
259 | time.sleep(SCAN_DEEP[SEARCH_LEVEL - 1])
260 | self.__get_page_html(url)
261 | return page_html.text
262 | except requests.ConnectionError, e:
263 | error_print("[!_!] ERROR INFO: There is '%s' problem in requesting html page." % str(e))
264 | exit()
265 | except requests.ReadTimeout:
266 | return ''
267 |
268 | def init():
269 | """
270 | Initialize GitPrey with module inspection and input inspection
271 | :return: None
272 | """
273 | try:
274 | imp.find_module('lxml')
275 | except ImportError:
276 | error_print('[!_!]ERROR INFO: You have to install lxml module.')
277 | exit()
278 |
279 | # Get command parameters for searching level and key words
280 | parser = argparse.ArgumentParser(description="Searching sensitive file and content in GitHub.")
281 | parser.add_argument("-l", "--level", type=int, choices=range(1, 6), default=1, metavar="level",
282 | help="Set search level within 1~5, default is 1.")
283 | parser.add_argument("-k", "--keywords", metavar="keywords", required=True,
284 | help="Set key words to search projects.")
285 | args = parser.parse_args()
286 |
287 | SEARCH_LEVEL = args.level if args.level else 1
288 | key_words = args.keywords if args.keywords else ""
289 |
290 | # Print GitPrey digital logo and version information.
291 | info_print(GitPrey.__doc__)
292 |
293 | keyword_output = "[^_^] START INFO: The key word for searching is: {keyword}"
294 | info_print(keyword_output.format(keyword=key_words))
295 |
296 | return key_words
297 |
298 |
299 | def project_miner(key_words):
300 | """
301 | Search projects for content and path inspection later.
302 | :param key_words: key words for searching
303 | :return:
304 | """
305 | # Search projects according to key words and searching level
306 | _gitprey = GitPrey(key_words)
307 | total_project_list = _gitprey.search_project()[:MAX_SEARCH_REPO]
308 |
309 | project_info_output = "\n[*_*] PROJECT INFO: Found {num} public projects related to the key words.\n"
310 | info_print(project_info_output.format(num=len(total_project_list)))
311 |
312 | if not total_project_list:
313 | return
314 |
315 | # Scan all projects with pattern content
316 | info_print("[^_^] START INFO: Begin searching sensitive content.")
317 | result = {}
318 | for i in xrange(len(total_project_list)/MAX_REPO_SINGLE_SEARCH+1):
319 | repo_list = total_project_list[i*MAX_REPO_SINGLE_SEARCH:i*MAX_REPO_SINGLE_SEARCH+MAX_REPO_SINGLE_SEARCH]
320 | # Join projects to together to search
321 |
322 | if len(repo_list) == 0: break
323 |
324 | repo_string = " repo:" + " repo:".join(repo_list)
325 | result.update(_gitprey.sensitive_info_query(repo_string))
326 | jsonify_result(result)
327 | info_print("[^_^] END INFO: Sensitive content searching is done.\n")
328 |
329 | def jsonify_result(result):
330 | with open('static/report.html', 'r') as f:
331 | html = f.read()
332 |
333 | data = []
334 | for repo, urls in result.iteritems():
335 | if not urls: continue
336 | children = []
337 | for url, codel in urls.iteritems():
338 | code = '\n'.join(codel)
339 | children.append({"code": code, "label": os.path.basename(url), "fileurl": url, "repo": repo})
340 | data.append({"children": children, "label": repo})
341 |
342 | html = html.replace('[];//gitleak_data_replace', json.dumps(data))
343 | with open('report.html', 'w') as f:
344 | f.write(html)
345 |
346 | if __name__ == "__main__":
347 | # Initialize key words input.
348 | key_words = init()
349 | # Search related projects depend on key words.
350 | project_miner(key_words)
351 |
--------------------------------------------------------------------------------
/static/jquery.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
2 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
3 | a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/