mirror of
https://github.com/michenriksen/gitrob.git
synced 2026-02-18 12:50:28 -06:00
Genesis.
This commit is contained in:
29
.github/ISSUE_TEMPLATE.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
Hey there and thank you for using the issue tracker!
|
||||
|
||||
## Checklist before filing an issue:
|
||||
|
||||
- [ ] Is this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome.
|
||||
- [ ] Have a usage question? Ask your question on [StackOverflow](http://stackoverflow.com), [StackExchange Security](https://security.stackexchange.com) or similar platform.
|
||||
- [ ] Have an idea for a feature? Make sure that it hasn't been suggested before and describe your idea in detail.
|
||||
|
||||
## None of the above? create a bug report
|
||||
|
||||
Make sure to add **all the information needed to understand the bug** so that someone can help. If information is missing, the issue will be labeled with 'Needs more information' and closed until there is enough information.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
|
||||
## Steps to Reproduce the Problem
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Specifications
|
||||
|
||||
- Gitrob version:
|
||||
- Operating system:
|
||||
- Go version:
|
||||
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
**IMPORTANT: Please do not create a Pull Request without creating an issue first.**
|
||||
|
||||
*Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request.*
|
||||
|
||||
Please provide enough information so that others can review your pull request:
|
||||
|
||||
<!-- You can skip this if you're fixing a typo or similar tiny fix. -->
|
||||
|
||||
Explain the **details** for making this change. What existing problem does the pull request solve?
|
||||
|
||||
<!-- Example: When "Adding a function to do X", explain why it is necessary to have a way to do X. -->
|
||||
|
||||
**Closing issues**
|
||||
|
||||
Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such).
|
||||
82
.gitignore
vendored
Normal file
82
.gitignore
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
gitrob
|
||||
gitrob.exe
|
||||
|
||||
build
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dropbox settings and caches
|
||||
.dropbox
|
||||
.dropbox.attr
|
||||
.dropbox.cache
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
|
||||
# TextMate
|
||||
*.tmproj
|
||||
*.tmproject
|
||||
tmtags
|
||||
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-v][a-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
24
CHANGELOG.md
Normal file
24
CHANGELOG.md
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## 2.0.0-beta - 2018-06-08
|
||||
### Added
|
||||
- Total rewrite of Gitrob in [Golang](https://golang.org/)
|
||||
- Find interesting files in history down to a default (and configurable) depth of 500 commits
|
||||
- Hexdump view for binary files
|
||||
- Saving and loading of session files for easy sharing
|
||||
|
||||
### Removed
|
||||
- All the stupid Rubygems with native extensions
|
||||
- PostgreSQL dependency
|
||||
- Messy assessment comparison feature
|
||||
- User overview
|
||||
- Repository overview
|
||||
|
||||
[Unreleased]: https://github.com/michenriksen/gitrob/compare/v2.0.0-beta...HEAD
|
||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Michael Henriksen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
66
README.md
Normal file
66
README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Gitrob: Putting the Open Source in OSINT
|
||||
|
||||
Gitrob is a tool to help find potentially sensitive files pushed to public repositories on Github. Gitrob will clone repositories belonging to a user or organization down to a configurable depth and iterate through the commit history and flag files that match signatures for potentially sensitive files. The findings will be presented through a web interface for easy browsing and analysis.
|
||||
|
||||
## Usage
|
||||
|
||||
gitrob [options] target [target2] ... [targetN]
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-bind-address string
|
||||
Address to bind web server to (default "127.0.0.1")
|
||||
-commit-depth int
|
||||
Number of repository commits to process (default 500)
|
||||
-debug
|
||||
Print debugging information
|
||||
-github-access-token string
|
||||
GitHub access token to use for API requests
|
||||
-load string
|
||||
Load session file
|
||||
-no-expand-orgs
|
||||
Don't add members to targets when processing organizations
|
||||
-port int
|
||||
Port to run web server on (default 9393)
|
||||
-save string
|
||||
Save session to file
|
||||
-silent
|
||||
Suppress all output except for errors
|
||||
-threads int
|
||||
Number of concurrent threads (default number of logical CPUs)
|
||||
```
|
||||
|
||||
### Saving session to a file
|
||||
|
||||
By default, gitrob will store its state for an assessment in memory. This means that the results of an assessment is lost when Gitrob is closed. You can save the session to a file by using the `-save` option:
|
||||
|
||||
gitrob -save ~/gitrob-session.json acmecorp
|
||||
|
||||
Gitrob will save all the gathered information to the specified file path as a special JSON document. The file can be loaded again for browsing at another point in time, shared with other analysts or parsed for custom integrations with other tools and systems.
|
||||
|
||||
### Loading session from a file
|
||||
|
||||
A session stored in a file can be loaded with the `-load` option:
|
||||
|
||||
gitrob -load ~/gitrob-session.json
|
||||
|
||||
Gitrob will start its web interface and serve the results for analysis.
|
||||
|
||||
## Installation
|
||||
|
||||
A [precompiled version is available](https://github.com/michenriksen/gitrob/releases) for each release, alternatively you can use the latest version of the source code from this repository in order to build your own binary.
|
||||
|
||||
Make sure you have a correctly configured **Go >= 1.8** environment and that `$GOPATH/bin` is in your `$PATH`
|
||||
|
||||
$ go get github.com/michenriksen/gitrob
|
||||
|
||||
This command will download gitrob, install its dependencies, compile it and move the `gitrob` executable to `$GOPATH/bin`.
|
||||
|
||||
### Github access token
|
||||
|
||||
Gitrob will need a Github access token in order to interact with the Github API. [Create a personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) and save it in an environment variable in your `.bashrc` or similar shell configuration file:
|
||||
|
||||
export GITROB_ACCESS_TOKEN=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
||||
|
||||
Alternatively you can specify the access token with the `-github-access-token` option, but watch out for your command history!
|
||||
59
build.sh
Executable file
59
build.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
BUILD_FOLDER=build
|
||||
VERSION=$(cat core/banner.go | grep Version | cut -d '"' -f 2)
|
||||
|
||||
bin_dep() {
|
||||
BIN=$1
|
||||
which $BIN > /dev/null || { echo "[-] Dependency $BIN not found !"; exit 1; }
|
||||
}
|
||||
|
||||
create_exe_archive() {
|
||||
bin_dep 'zip'
|
||||
|
||||
OUTPUT=$1
|
||||
|
||||
echo "[*] Creating archive $OUTPUT ..."
|
||||
zip -j "$OUTPUT" gitrob.exe ../README.md ../LICENSE.txt > /dev/null
|
||||
rm -rf gitrob gitrob.exe
|
||||
}
|
||||
|
||||
create_archive() {
|
||||
bin_dep 'zip'
|
||||
|
||||
OUTPUT=$1
|
||||
|
||||
echo "[*] Creating archive $OUTPUT ..."
|
||||
zip -j "$OUTPUT" gitrob ../README.md ../LICENSE.md > /dev/null
|
||||
rm -rf gitrob gitrob.exe
|
||||
}
|
||||
|
||||
build_linux_amd64() {
|
||||
echo "[*] Building linux/amd64 ..."
|
||||
GOOS=linux GOARCH=amd64 go build -o gitrob ..
|
||||
}
|
||||
|
||||
build_macos_amd64() {
|
||||
echo "[*] Building darwin/amd64 ..."
|
||||
GOOS=darwin GOARCH=amd64 go build -o gitrob ..
|
||||
}
|
||||
|
||||
build_windows_amd64() {
|
||||
echo "[*] Building windows/amd64 ..."
|
||||
GOOS=windows GOARCH=amd64 go build -o gitrob.exe ..
|
||||
}
|
||||
|
||||
rm -rf $BUILD_FOLDER
|
||||
mkdir $BUILD_FOLDER
|
||||
cd $BUILD_FOLDER
|
||||
|
||||
build_linux_amd64 && create_archive gitrob_linux_amd64_$VERSION.zip
|
||||
build_macos_amd64 && create_archive gitrob_macos_amd64_$VERSION.zip
|
||||
build_windows_amd64 && create_exe_archive gitrob_windows_amd64_$VERSION.zip
|
||||
shasum -a 256 * > checksums.txt
|
||||
|
||||
echo
|
||||
echo
|
||||
du -sh *
|
||||
|
||||
cd --
|
||||
13
core/banner.go
Normal file
13
core/banner.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package core
|
||||
|
||||
const (
|
||||
Name = "gitrob"
|
||||
Version = "2.0.0-beta"
|
||||
Author = "Michael Henriksen"
|
||||
Website = "https://github.com/michenriksen/gitrob"
|
||||
ASCIIBanner = " _ __ __\n" +
|
||||
" ___ _(_) /________ / /\n" +
|
||||
" / _ `/ / __/ __/ _ \\/ _ \\\n" +
|
||||
" \\_, /_/\\__/_/ \\___/_.__/\n" +
|
||||
"/___/ by @michenriksen"
|
||||
)
|
||||
808
core/bindata.go
Normal file
808
core/bindata.go
Normal file
File diff suppressed because one or more lines are too long
33
core/core.go
Normal file
33
core/core.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var NewlineRegex = regexp.MustCompile(`\r?\n`)
|
||||
|
||||
func FileExists(path string) bool {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Pluralize(count int, singular string, plural string) string {
|
||||
if count == 1 {
|
||||
return singular
|
||||
}
|
||||
return plural
|
||||
}
|
||||
|
||||
func TruncateString(str string, maxLength int) string {
|
||||
str = NewlineRegex.ReplaceAllString(str, " ")
|
||||
str = strings.TrimSpace(str)
|
||||
if len(str) > maxLength {
|
||||
str = fmt.Sprintf("%s...", str[0:maxLength])
|
||||
}
|
||||
return str
|
||||
}
|
||||
120
core/git.go
Normal file
120
core/git.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"gopkg.in/src-d/go-git.v4/utils/merkletrie"
|
||||
)
|
||||
|
||||
const (
|
||||
EmptyTreeCommitId = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
||||
)
|
||||
|
||||
func CloneRepository(url *string, branch *string, depth int) (*git.Repository, string, error) {
|
||||
urlVal := *url
|
||||
branchVal := *branch
|
||||
dir, err := ioutil.TempDir("", "gitrob")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
repository, err := git.PlainClone(dir, false, &git.CloneOptions{
|
||||
URL: urlVal,
|
||||
Depth: depth,
|
||||
ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", branchVal)),
|
||||
SingleBranch: true,
|
||||
Tags: git.NoTags,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, dir, err
|
||||
}
|
||||
return repository, dir, nil
|
||||
}
|
||||
|
||||
func GetRepositoryHistory(repository *git.Repository) ([]*object.Commit, error) {
|
||||
var commits []*object.Commit
|
||||
ref, err := repository.Head()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cIter, err := repository.Log(&git.LogOptions{From: ref.Hash()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cIter.ForEach(func(c *object.Commit) error {
|
||||
commits = append(commits, c)
|
||||
return nil
|
||||
})
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
func GetChanges(commit *object.Commit, repo *git.Repository) (object.Changes, error) {
|
||||
parentCommit, err := GetParentCommit(commit, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commitTree, err := commit.Tree()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parentCommitTree, err := parentCommit.Tree()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
changes, err := object.DiffTree(parentCommitTree, commitTree)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func GetParentCommit(commit *object.Commit, repo *git.Repository) (*object.Commit, error) {
|
||||
if commit.NumParents() == 0 {
|
||||
parentCommit, err := repo.CommitObject(plumbing.NewHash(EmptyTreeCommitId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parentCommit, nil
|
||||
}
|
||||
parentCommit, err := commit.Parents().Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parentCommit, nil
|
||||
}
|
||||
|
||||
func GetChangeAction(change *object.Change) string {
|
||||
action, err := change.Action()
|
||||
if err != nil {
|
||||
return "Unknown"
|
||||
}
|
||||
switch action {
|
||||
case merkletrie.Insert:
|
||||
return "Insert"
|
||||
case merkletrie.Modify:
|
||||
return "Modify"
|
||||
case merkletrie.Delete:
|
||||
return "Delete"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func GetChangePath(change *object.Change) string {
|
||||
action, err := change.Action()
|
||||
if err != nil {
|
||||
return change.To.Name
|
||||
}
|
||||
|
||||
if action == merkletrie.Delete {
|
||||
return change.From.Name
|
||||
} else {
|
||||
return change.To.Name
|
||||
}
|
||||
}
|
||||
113
core/github.go
Normal file
113
core/github.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
)
|
||||
|
||||
type GithubOwner struct {
|
||||
Login *string
|
||||
ID *int
|
||||
Type *string
|
||||
Name *string
|
||||
AvatarURL *string
|
||||
URL *string
|
||||
Company *string
|
||||
Blog *string
|
||||
Location *string
|
||||
Email *string
|
||||
Bio *string
|
||||
}
|
||||
|
||||
type GithubRepository struct {
|
||||
Owner *string
|
||||
ID *int
|
||||
Name *string
|
||||
FullName *string
|
||||
CloneURL *string
|
||||
URL *string
|
||||
DefaultBranch *string
|
||||
Description *string
|
||||
Homepage *string
|
||||
}
|
||||
|
||||
func GetUserOrOrganization(login string, client *github.Client) (*GithubOwner, error) {
|
||||
ctx := context.Background()
|
||||
user, _, err := client.Users.Get(ctx, login)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &GithubOwner{
|
||||
Login: user.Login,
|
||||
ID: user.ID,
|
||||
Type: user.Type,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
URL: user.HTMLURL,
|
||||
Company: user.Company,
|
||||
Blog: user.Blog,
|
||||
Location: user.Location,
|
||||
Email: user.Email,
|
||||
Bio: user.Bio,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetRepositoriesFromOwner(login *string, client *github.Client) ([]*GithubRepository, error) {
|
||||
var allRepos []*GithubRepository
|
||||
loginVal := *login
|
||||
ctx := context.Background()
|
||||
opt := &github.RepositoryListOptions{
|
||||
Type: "sources",
|
||||
}
|
||||
|
||||
for {
|
||||
repos, resp, err := client.Repositories.List(ctx, loginVal, opt)
|
||||
if err != nil {
|
||||
return allRepos, err
|
||||
}
|
||||
for _, repo := range repos {
|
||||
if !*repo.Fork {
|
||||
r := GithubRepository{
|
||||
Owner: repo.Owner.Login,
|
||||
ID: repo.ID,
|
||||
Name: repo.Name,
|
||||
FullName: repo.FullName,
|
||||
CloneURL: repo.CloneURL,
|
||||
URL: repo.HTMLURL,
|
||||
DefaultBranch: repo.DefaultBranch,
|
||||
Description: repo.Description,
|
||||
Homepage: repo.Homepage,
|
||||
}
|
||||
allRepos = append(allRepos, &r)
|
||||
}
|
||||
}
|
||||
if resp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opt.Page = resp.NextPage
|
||||
}
|
||||
|
||||
return allRepos, nil
|
||||
}
|
||||
|
||||
func GetOrganizationMembers(login *string, client *github.Client) ([]*GithubOwner, error) {
|
||||
var allMembers []*GithubOwner
|
||||
loginVal := *login
|
||||
ctx := context.Background()
|
||||
opt := &github.ListMembersOptions{}
|
||||
for {
|
||||
members, resp, err := client.Organizations.ListMembers(ctx, loginVal, opt)
|
||||
if err != nil {
|
||||
return allMembers, err
|
||||
}
|
||||
for _, member := range members {
|
||||
allMembers = append(allMembers, &GithubOwner{Login: member.Login, ID: member.ID, Type: member.Type})
|
||||
}
|
||||
if resp.NextPage == 0 {
|
||||
break
|
||||
}
|
||||
opt.Page = resp.NextPage
|
||||
}
|
||||
return allMembers, nil
|
||||
}
|
||||
85
core/log.go
Normal file
85
core/log.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const (
|
||||
FATAL = 5
|
||||
ERROR = 4
|
||||
WARN = 3
|
||||
IMPORTANT = 2
|
||||
INFO = 1
|
||||
DEBUG = 0
|
||||
)
|
||||
|
||||
var LogColors = map[int]*color.Color{
|
||||
FATAL: color.New(color.FgRed).Add(color.Bold),
|
||||
ERROR: color.New(color.FgRed),
|
||||
WARN: color.New(color.FgYellow),
|
||||
IMPORTANT: color.New(color.Bold),
|
||||
DEBUG: color.New(color.FgCyan).Add(color.Faint),
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
sync.Mutex
|
||||
|
||||
debug bool
|
||||
silent bool
|
||||
}
|
||||
|
||||
func (l *Logger) SetSilent(s bool) {
|
||||
l.silent = s
|
||||
}
|
||||
|
||||
func (l *Logger) SetDebug(d bool) {
|
||||
l.debug = d
|
||||
}
|
||||
|
||||
func (l *Logger) Log(level int, format string, args ...interface{}) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
if level == DEBUG && l.debug == false {
|
||||
return
|
||||
} else if level < ERROR && l.silent == true {
|
||||
return
|
||||
}
|
||||
|
||||
if c, ok := LogColors[level]; ok {
|
||||
c.Printf(format, args...)
|
||||
} else {
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
if level == FATAL {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||
l.Log(FATAL, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(format string, args ...interface{}) {
|
||||
l.Log(ERROR, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(format string, args ...interface{}) {
|
||||
l.Log(WARN, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Important(format string, args ...interface{}) {
|
||||
l.Log(IMPORTANT, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(format string, args ...interface{}) {
|
||||
l.Log(INFO, format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
||||
l.Log(DEBUG, format, args...)
|
||||
}
|
||||
39
core/options.go
Normal file
39
core/options.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
CommitDepth *int
|
||||
GithubAccessToken *string `json:"-"`
|
||||
NoExpandOrgs *bool
|
||||
Threads *int
|
||||
Save *string `json:"-"`
|
||||
Load *string `json:"-"`
|
||||
BindAddress *string
|
||||
Port *int
|
||||
Silent *bool
|
||||
Debug *bool
|
||||
Logins []string
|
||||
}
|
||||
|
||||
func ParseOptions() (Options, error) {
|
||||
options := Options{
|
||||
CommitDepth: flag.Int("commit-depth", 500, "Number of repository commits to process"),
|
||||
GithubAccessToken: flag.String("github-access-token", "", "GitHub access token to use for API requests"),
|
||||
NoExpandOrgs: flag.Bool("no-expand-orgs", false, "Don't add members to targets when processing organizations"),
|
||||
Threads: flag.Int("threads", 0, "Number of concurrent threads (default number of logical CPUs)"),
|
||||
Save: flag.String("save", "", "Save session to file"),
|
||||
Load: flag.String("load", "", "Load session file"),
|
||||
BindAddress: flag.String("bind-address", "127.0.0.1", "Address to bind web server to"),
|
||||
Port: flag.Int("port", 9393, "Port to run web server on"),
|
||||
Silent: flag.Bool("silent", false, "Suppress all output except for errors"),
|
||||
Debug: flag.Bool("debug", false, "Print debugging information"),
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
options.Logins = flag.Args()
|
||||
|
||||
return options, nil
|
||||
}
|
||||
124
core/router.go
Normal file
124
core/router.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/gin-contrib/secure"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
GithubBaseUri = "https://raw.githubusercontent.com"
|
||||
MaximumFileSize = 102400
|
||||
CspPolicy = "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
|
||||
ReferrerPolicy = "no-referrer"
|
||||
)
|
||||
|
||||
type binaryFileSystem struct {
|
||||
fs http.FileSystem
|
||||
}
|
||||
|
||||
func (b *binaryFileSystem) Open(name string) (http.File, error) {
|
||||
return b.fs.Open(name)
|
||||
}
|
||||
|
||||
func (b *binaryFileSystem) Exists(prefix string, filepath string) bool {
|
||||
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
|
||||
if _, err := b.fs.Open(p); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func BinaryFileSystem(root string) *binaryFileSystem {
|
||||
fs := &assetfs.AssetFS{Asset, AssetDir, AssetInfo, root}
|
||||
return &binaryFileSystem{
|
||||
fs,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouter(s *Session) *gin.Engine {
|
||||
if *s.Options.Debug == true {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
} else {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.Use(static.Serve("/", BinaryFileSystem("static")))
|
||||
router.Use(secure.New(secure.Config{
|
||||
SSLRedirect: false,
|
||||
IsDevelopment: false,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXssFilter: true,
|
||||
ContentSecurityPolicy: CspPolicy,
|
||||
ReferrerPolicy: ReferrerPolicy,
|
||||
}))
|
||||
router.GET("/stats", func(c *gin.Context) {
|
||||
c.JSON(200, s.Stats)
|
||||
})
|
||||
router.GET("/findings", func(c *gin.Context) {
|
||||
c.JSON(200, s.Findings)
|
||||
})
|
||||
router.GET("/targets", func(c *gin.Context) {
|
||||
c.JSON(200, s.Targets)
|
||||
})
|
||||
router.GET("/repositories", func(c *gin.Context) {
|
||||
c.JSON(200, s.Repositories)
|
||||
})
|
||||
router.GET("/files/:owner/:repo/:commit/*path", fetchFile)
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
func fetchFile(c *gin.Context) {
|
||||
fileUrl := fmt.Sprintf("%s/%s/%s/%s%s", GithubBaseUri, c.Param("owner"), c.Param("repo"), c.Param("commit"), c.Param("path"))
|
||||
resp, err := http.Head(fileUrl)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"message": "No content",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if resp.ContentLength > MaximumFileSize {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{
|
||||
"message": fmt.Sprintf("File size exceeds maximum of %d bytes", MaximumFileSize),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err = http.Get(fileUrl)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, string(body[:]))
|
||||
}
|
||||
242
core/session.go
Normal file
242
core/session.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/go-github/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const (
|
||||
AccessTokenEnvVariable = "GITROB_ACCESS_TOKEN"
|
||||
|
||||
StatusInitializing = "initializing"
|
||||
StatusGathering = "gathering"
|
||||
StatusAnalyzing = "analyzing"
|
||||
StatusFinished = "finished"
|
||||
)
|
||||
|
||||
type Stats struct {
|
||||
sync.Mutex
|
||||
|
||||
StartedAt time.Time
|
||||
FinishedAt time.Time
|
||||
Status string
|
||||
Progress float64
|
||||
Targets int
|
||||
Repositories int
|
||||
Commits int
|
||||
Files int
|
||||
Findings int
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
sync.Mutex
|
||||
|
||||
Version string
|
||||
Options Options `json:"-"`
|
||||
Out *Logger `json:"-"`
|
||||
Stats *Stats
|
||||
GithubAccessToken string `json:"-"`
|
||||
GithubClient *github.Client `json:"-"`
|
||||
Router *gin.Engine `json:"-"`
|
||||
Targets []*GithubOwner
|
||||
Repositories []*GithubRepository
|
||||
Findings []*Finding
|
||||
}
|
||||
|
||||
func (s *Session) Start() {
|
||||
s.InitStats()
|
||||
s.InitLogger()
|
||||
s.InitThreads()
|
||||
s.InitGithubAccessToken()
|
||||
s.InitGithubClient()
|
||||
s.InitRouter()
|
||||
}
|
||||
|
||||
func (s *Session) Finish() {
|
||||
s.Stats.FinishedAt = time.Now()
|
||||
s.Stats.Status = StatusFinished
|
||||
}
|
||||
|
||||
func (s *Session) AddTarget(target *GithubOwner) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
for _, t := range s.Targets {
|
||||
if *target.ID == *t.ID {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.Targets = append(s.Targets, target)
|
||||
}
|
||||
|
||||
func (s *Session) AddRepository(repository *GithubRepository) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
for _, r := range s.Repositories {
|
||||
if *repository.ID == *r.ID {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.Repositories = append(s.Repositories, repository)
|
||||
}
|
||||
|
||||
func (s *Session) AddFinding(finding *Finding) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.Findings = append(s.Findings, finding)
|
||||
}
|
||||
|
||||
func (s *Session) InitStats() {
|
||||
if s.Stats != nil {
|
||||
return
|
||||
}
|
||||
s.Stats = &Stats{
|
||||
StartedAt: time.Now(),
|
||||
Status: StatusInitializing,
|
||||
Progress: 0.0,
|
||||
Targets: 0,
|
||||
Repositories: 0,
|
||||
Commits: 0,
|
||||
Files: 0,
|
||||
Findings: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) InitLogger() {
|
||||
s.Out = &Logger{}
|
||||
s.Out.SetDebug(*s.Options.Debug)
|
||||
s.Out.SetSilent(*s.Options.Silent)
|
||||
}
|
||||
|
||||
func (s *Session) InitGithubAccessToken() {
|
||||
if *s.Options.GithubAccessToken == "" {
|
||||
accessToken := os.Getenv(AccessTokenEnvVariable)
|
||||
if accessToken == "" {
|
||||
s.Out.Fatal("No GitHub access token given. Please provide via command line option or in the %s environment variable.\n", AccessTokenEnvVariable)
|
||||
}
|
||||
s.GithubAccessToken = accessToken
|
||||
} else {
|
||||
s.GithubAccessToken = *s.Options.GithubAccessToken
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) InitGithubClient() {
|
||||
ctx := context.Background()
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: s.GithubAccessToken},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
s.GithubClient = github.NewClient(tc)
|
||||
s.GithubClient.UserAgent = fmt.Sprintf("%s v%s", Name, Version)
|
||||
}
|
||||
|
||||
func (s *Session) InitThreads() {
|
||||
if *s.Options.Threads == 0 {
|
||||
numCPUs := runtime.NumCPU()
|
||||
s.Options.Threads = &numCPUs
|
||||
}
|
||||
runtime.GOMAXPROCS(*s.Options.Threads + 2) // thread count + main + web server
|
||||
}
|
||||
|
||||
func (s *Session) InitRouter() {
|
||||
bind := fmt.Sprintf("%s:%d", *s.Options.BindAddress, *s.Options.Port)
|
||||
s.Router = NewRouter(s)
|
||||
go func(sess *Session) {
|
||||
if err := sess.Router.Run(bind); err != nil {
|
||||
sess.Out.Fatal("Error when starting web server: %s\n", err)
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
|
||||
func (s *Session) SaveToFile(location string) error {
|
||||
sessionJson, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(location, sessionJson, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stats) IncrementTargets() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.Targets++
|
||||
}
|
||||
|
||||
func (s *Stats) IncrementRepositories() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.Repositories++
|
||||
}
|
||||
|
||||
func (s *Stats) IncrementCommits() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.Commits++
|
||||
}
|
||||
|
||||
func (s *Stats) IncrementFiles() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.Files++
|
||||
}
|
||||
|
||||
func (s *Stats) IncrementFindings() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.Findings++
|
||||
}
|
||||
|
||||
func (s *Stats) UpdateProgress(current int, total int) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if current >= total {
|
||||
s.Progress = 100.0
|
||||
} else {
|
||||
s.Progress = (float64(current) * float64(100)) / float64(total)
|
||||
}
|
||||
}
|
||||
|
||||
func NewSession() (*Session, error) {
|
||||
var err error
|
||||
var session Session
|
||||
|
||||
if session.Options, err = ParseOptions(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *session.Options.Save != "" && FileExists(*session.Options.Save) {
|
||||
return nil, errors.New(fmt.Sprintf("File: %s already exists.", *session.Options.Save))
|
||||
}
|
||||
|
||||
if *session.Options.Load != "" {
|
||||
if !FileExists(*session.Options.Load) {
|
||||
return nil, errors.New(fmt.Sprintf("Session file %s does not exist or is not readable.", *session.Options.Load))
|
||||
}
|
||||
data, err := ioutil.ReadFile(*session.Options.Load)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(data, &session); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Session file %s is corrupt or generated by an old version of Gitrob.", *session.Options.Load))
|
||||
}
|
||||
}
|
||||
|
||||
session.Version = Version
|
||||
session.Start()
|
||||
|
||||
return &session, nil
|
||||
}
|
||||
690
core/signatures.go
Normal file
690
core/signatures.go
Normal file
@@ -0,0 +1,690 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
TypeSimple = "simple"
|
||||
TypePattern = "pattern"
|
||||
|
||||
PartExtension = "extension"
|
||||
PartFilename = "filename"
|
||||
PartPath = "path"
|
||||
)
|
||||
|
||||
type MatchFile struct {
|
||||
Path string
|
||||
Filename string
|
||||
Extension string
|
||||
}
|
||||
|
||||
type Finding struct {
|
||||
Id string
|
||||
FilePath string
|
||||
Action string
|
||||
Description string
|
||||
Comment string
|
||||
RepositoryOwner string
|
||||
RepositoryName string
|
||||
CommitHash string
|
||||
CommitMessage string
|
||||
CommitAuthor string
|
||||
FileUrl string
|
||||
CommitUrl string
|
||||
RepositoryUrl string
|
||||
}
|
||||
|
||||
func (f *Finding) setupUrls() {
|
||||
f.RepositoryUrl = fmt.Sprintf("https://github.com/%s/%s", f.RepositoryOwner, f.RepositoryName)
|
||||
f.FileUrl = fmt.Sprintf("%s/blob/%s/%s", f.RepositoryUrl, f.CommitHash, f.FilePath)
|
||||
f.CommitUrl = fmt.Sprintf("%s/commit/%s", f.RepositoryUrl, f.CommitHash)
|
||||
}
|
||||
|
||||
func (f *Finding) generateID() {
|
||||
h := sha1.New()
|
||||
io.WriteString(h, f.FilePath)
|
||||
io.WriteString(h, f.Action)
|
||||
io.WriteString(h, f.RepositoryOwner)
|
||||
io.WriteString(h, f.RepositoryName)
|
||||
io.WriteString(h, f.CommitHash)
|
||||
io.WriteString(h, f.CommitMessage)
|
||||
io.WriteString(h, f.CommitAuthor)
|
||||
f.Id = fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
func (f *Finding) Initialize() {
|
||||
f.setupUrls()
|
||||
f.generateID()
|
||||
}
|
||||
|
||||
type Signature interface {
|
||||
Match(file MatchFile) bool
|
||||
Description() string
|
||||
Comment() string
|
||||
}
|
||||
|
||||
type SimpleSignature struct {
|
||||
part string
|
||||
match string
|
||||
description string
|
||||
comment string
|
||||
}
|
||||
|
||||
type PatternSignature struct {
|
||||
part string
|
||||
match *regexp.Regexp
|
||||
description string
|
||||
comment string
|
||||
}
|
||||
|
||||
func (s SimpleSignature) Match(file MatchFile) bool {
|
||||
var haystack *string
|
||||
switch s.part {
|
||||
case PartPath:
|
||||
haystack = &file.Path
|
||||
case PartFilename:
|
||||
haystack = &file.Filename
|
||||
case PartExtension:
|
||||
haystack = &file.Extension
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return (s.match == *haystack)
|
||||
}
|
||||
|
||||
func (s SimpleSignature) Description() string {
|
||||
return s.description
|
||||
}
|
||||
|
||||
func (s SimpleSignature) Comment() string {
|
||||
return s.comment
|
||||
}
|
||||
|
||||
func (s PatternSignature) Match(file MatchFile) bool {
|
||||
var haystack *string
|
||||
switch s.part {
|
||||
case PartPath:
|
||||
haystack = &file.Path
|
||||
case PartFilename:
|
||||
haystack = &file.Filename
|
||||
case PartExtension:
|
||||
haystack = &file.Extension
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return s.match.MatchString(*haystack)
|
||||
}
|
||||
|
||||
func (s PatternSignature) Description() string {
|
||||
return s.description
|
||||
}
|
||||
|
||||
func (s PatternSignature) Comment() string {
|
||||
return s.comment
|
||||
}
|
||||
|
||||
func NewMatchFile(path string) MatchFile {
|
||||
_, filename := filepath.Split(path)
|
||||
extension := filepath.Ext(path)
|
||||
return MatchFile{
|
||||
Path: path,
|
||||
Filename: filename,
|
||||
Extension: extension,
|
||||
}
|
||||
}
|
||||
|
||||
var Signatures = []Signature{
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".pem",
|
||||
description: "Potential cryptographic private key",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".log",
|
||||
description: "Log file",
|
||||
comment: "Log files can contain secret HTTP endpoints, session IDs, API keys and other goodies",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".pkcs12",
|
||||
description: "Potential cryptographic key bundle",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".p12",
|
||||
description: "Potential cryptographic key bundle",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".pfx",
|
||||
description: "Potential cryptographic key bundle",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".asc",
|
||||
description: "Potential cryptographic key bundle",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "otr.private_key",
|
||||
description: "Pidgin OTR private key",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".ovpn",
|
||||
description: "OpenVPN client configuration file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".cscfg",
|
||||
description: "Azure service configuration schema file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".rdp",
|
||||
description: "Remote Desktop connection file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".mdf",
|
||||
description: "Microsoft SQL database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".sdf",
|
||||
description: "Microsoft SQL server compact database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".sqlite",
|
||||
description: "SQLite database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".bek",
|
||||
description: "Microsoft BitLocker recovery key file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".tpm",
|
||||
description: "Microsoft BitLocker Trusted Platform Module password file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".fve",
|
||||
description: "Windows BitLocker full volume encrypted data file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".jks",
|
||||
description: "Java keystore file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".psafe3",
|
||||
description: "Password Safe database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "secret_token.rb",
|
||||
description: "Ruby On Rails secret token configuration file",
|
||||
comment: "If the Rails secret token is known, it can allow for remote code execution (http://www.exploit-db.com/exploits/27527/)",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "carrierwave.rb",
|
||||
description: "Carrierwave configuration file",
|
||||
comment: "Can contain credentials for cloud storage systems such as Amazon S3 and Google Storage",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "database.yml",
|
||||
description: "Potential Ruby On Rails database configuration file",
|
||||
comment: "Can contain database credentials",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "omniauth.rb",
|
||||
description: "OmniAuth configuration file",
|
||||
comment: "The OmniAuth configuration file can contain client application secrets",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "settings.py",
|
||||
description: "Django configuration file",
|
||||
comment: "Can contain database credentials, cloud storage system credentials, and other secrets",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".agilekeychain",
|
||||
description: "1Password password manager database file",
|
||||
comment: "Feed it to Hashcat and see if you're lucky",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".keychain",
|
||||
description: "Apple Keychain database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".pcap",
|
||||
description: "Network traffic capture file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".gnucash",
|
||||
description: "GnuCash database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "jenkins.plugins.publish_over_ssh.BapSshPublisherPlugin.xml",
|
||||
description: "Jenkins publish over SSH plugin file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "credentials.xml",
|
||||
description: "Potential Jenkins credentials file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".kwallet",
|
||||
description: "KDE Wallet Manager database file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "LocalSettings.php",
|
||||
description: "Potential MediaWiki configuration file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".tblk",
|
||||
description: "Tunnelblick VPN configuration file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "Favorites.plist",
|
||||
description: "Sequel Pro MySQL database manager bookmark file",
|
||||
comment: "",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "configuration.user.xpl",
|
||||
description: "Little Snitch firewall configuration file",
|
||||
comment: "Contains traffic rules for applications",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartExtension,
|
||||
match: ".dayone",
|
||||
description: "Day One journal file",
|
||||
comment: "Now it's getting creepy...",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "journal.txt",
|
||||
description: "Potential jrnl journal file",
|
||||
comment: "Now it's getting creepy...",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "knife.rb",
|
||||
description: "Chef Knife configuration file",
|
||||
comment: "Can contain references to Chef servers",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "proftpdpasswd",
|
||||
description: "cPanel backup ProFTPd credentials file",
|
||||
comment: "Contains usernames and password hashes for FTP accounts",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "robomongo.json",
|
||||
description: "Robomongo MongoDB manager configuration file",
|
||||
comment: "Can contain credentials for MongoDB databases",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "filezilla.xml",
|
||||
description: "FileZilla FTP configuration file",
|
||||
comment: "Can contain credentials for FTP servers",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "recentservers.xml",
|
||||
description: "FileZilla FTP recent servers file",
|
||||
comment: "Can contain credentials for FTP servers",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "ventrilo_srv.ini",
|
||||
description: "Ventrilo server configuration file",
|
||||
comment: "Can contain passwords",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: "terraform.tfvars",
|
||||
description: "Terraform variable config file",
|
||||
comment: "Can contain credentials for terraform providers",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: ".exports",
|
||||
description: "Shell configuration file",
|
||||
comment: "Shell configuration files can contain passwords, API keys, hostnames and other goodies",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: ".functions",
|
||||
description: "Shell configuration file",
|
||||
comment: "Shell configuration files can contain passwords, API keys, hostnames and other goodies",
|
||||
},
|
||||
SimpleSignature{
|
||||
part: PartFilename,
|
||||
match: ".extra",
|
||||
description: "Shell configuration file",
|
||||
comment: "Shell configuration files can contain passwords, API keys, hostnames and other goodies",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^.*_rsa$`),
|
||||
description: "Private SSH key",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^.*_dsa$`),
|
||||
description: "Private SSH key",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^.*_ed25519$`),
|
||||
description: "Private SSH key",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^.*_ecdsa$`),
|
||||
description: "Private SSH key",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?ssh/config$`),
|
||||
description: "SSH configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartExtension,
|
||||
match: regexp.MustCompile(`^key(pair)?$`),
|
||||
description: "Potential cryptographic private key",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?(bash_|zsh_|sh_|z)?history$`),
|
||||
description: "Shell command history file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?mysql_history$`),
|
||||
description: "MySQL client command history file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?psql_history$`),
|
||||
description: "PostgreSQL client command history file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?pgpass$`),
|
||||
description: "PostgreSQL password file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?irb_history$`),
|
||||
description: "Ruby IRB console history file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?purple/accounts\.xml$`),
|
||||
description: "Pidgin chat client account configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?xchat2?/servlist_?\.conf$`),
|
||||
description: "Hexchat/XChat IRC client server list configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?irssi/config$`),
|
||||
description: "Irssi IRC client configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?recon-ng/keys\.db$`),
|
||||
description: "Recon-ng web reconnaissance framework API key database",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?dbeaver-data-sources.xml$`),
|
||||
description: "DBeaver SQL database manager configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?muttrc$`),
|
||||
description: "Mutt e-mail client configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?s3cfg$`),
|
||||
description: "S3cmd configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?aws/credentials$`),
|
||||
description: "AWS CLI credentials file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^sftp-config(\.json)?$`),
|
||||
description: "SFTP connection configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?trc$`),
|
||||
description: "T command-line Twitter client configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?gitrobrc$`),
|
||||
description: "Well, this is awkward... Gitrob configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?(bash|zsh|csh)rc$`),
|
||||
description: "Shell configuration file",
|
||||
comment: "Shell configuration files can contain passwords, API keys, hostnames and other goodies",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?(bash_|zsh_)?profile$`),
|
||||
description: "Shell profile configuration file",
|
||||
comment: "Shell configuration files can contain passwords, API keys, hostnames and other goodies",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?(bash_|zsh_)?aliases$`),
|
||||
description: "Shell command alias configuration file",
|
||||
comment: "Shell configuration files can contain passwords, API keys, hostnames and other goodies",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`config(\.inc)?\.php$`),
|
||||
description: "PHP configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartExtension,
|
||||
match: regexp.MustCompile(`^key(store|ring)$`),
|
||||
description: "GNOME Keyring database file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartExtension,
|
||||
match: regexp.MustCompile(`^kdbx?$`),
|
||||
description: "KeePass password manager database file",
|
||||
comment: "Feed it to Hashcat and see if you're lucky",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartExtension,
|
||||
match: regexp.MustCompile(`^sql(dump)?$`),
|
||||
description: "SQL dump file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?htpasswd$`),
|
||||
description: "Apache htpasswd file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^(\.|_)?netrc$`),
|
||||
description: "Configuration file for auto-login process",
|
||||
comment: "Can contain username and password",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?gem/credentials$`),
|
||||
description: "Rubygems credentials file",
|
||||
comment: "Can contain API key for a rubygems.org account",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?tugboat$`),
|
||||
description: "Tugboat DigitalOcean management tool configuration",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`doctl/config.yaml$`),
|
||||
description: "DigitalOcean doctl command-line client configuration file",
|
||||
comment: "Contains DigitalOcean API key and other information",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?git-credentials$`),
|
||||
description: "git-credential-store helper credentials file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`config/hub$`),
|
||||
description: "GitHub Hub command-line client configuration file",
|
||||
comment: "Can contain GitHub API access token",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?gitconfig$`),
|
||||
description: "Git configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`\.?chef/(.*)\.pem$`),
|
||||
description: "Chef private key",
|
||||
comment: "Can be used to authenticate against Chef servers",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`etc/shadow$`),
|
||||
description: "Potential Linux shadow file",
|
||||
comment: "Contains hashed passwords for system users",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`etc/passwd$`),
|
||||
description: "Potential Linux passwd file",
|
||||
comment: "Contains system user information",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?dockercfg$`),
|
||||
description: "Docker configuration file",
|
||||
comment: "Can contain credentials for public or private Docker registries",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?npmrc$`),
|
||||
description: "NPM configuration file",
|
||||
comment: "Can contain credentials for NPM registries",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartFilename,
|
||||
match: regexp.MustCompile(`^\.?env$`),
|
||||
description: "Environment configuration file",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`credential`),
|
||||
description: "Contains word: credential",
|
||||
comment: "",
|
||||
},
|
||||
PatternSignature{
|
||||
part: PartPath,
|
||||
match: regexp.MustCompile(`password`),
|
||||
description: "Contains word: password",
|
||||
comment: "",
|
||||
},
|
||||
}
|
||||
243
main.go
Normal file
243
main.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/michenriksen/gitrob/core"
|
||||
)
|
||||
|
||||
var (
|
||||
sess *core.Session
|
||||
err error
|
||||
)
|
||||
|
||||
func GatherTargets(sess *core.Session) {
|
||||
sess.Stats.Status = core.StatusGathering
|
||||
sess.Out.Important("Gathering targets...\n")
|
||||
for _, login := range sess.Options.Logins {
|
||||
target, err := core.GetUserOrOrganization(login, sess.GithubClient)
|
||||
if err != nil {
|
||||
sess.Out.Error(" Error retrieving information on %s: %s\n", login, err)
|
||||
continue
|
||||
}
|
||||
sess.Out.Debug("%s (ID: %d) type: %s\n", *target.Login, *target.ID, *target.Type)
|
||||
sess.AddTarget(target)
|
||||
if *sess.Options.NoExpandOrgs == false && *target.Type == "Organization" {
|
||||
sess.Out.Debug("Gathering members of %s (ID: %d)...\n", *target.Login, *target.ID)
|
||||
members, err := core.GetOrganizationMembers(target.Login, sess.GithubClient)
|
||||
if err != nil {
|
||||
sess.Out.Error(" Error retrieving members of %s: %s\n", *target.Login, err)
|
||||
continue
|
||||
}
|
||||
for _, member := range members {
|
||||
sess.Out.Debug("Adding organization member %s (ID: %d) to targets\n", *member.Login, *member.ID)
|
||||
sess.AddTarget(member)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GatherRepositories(sess *core.Session) {
|
||||
var ch = make(chan *core.GithubOwner, len(sess.Targets))
|
||||
var wg sync.WaitGroup
|
||||
var threadNum int
|
||||
if len(sess.Targets) == 1 {
|
||||
threadNum = 1
|
||||
} else if len(sess.Targets) <= *sess.Options.Threads {
|
||||
threadNum = len(sess.Targets) - 1
|
||||
} else {
|
||||
threadNum = *sess.Options.Threads
|
||||
}
|
||||
wg.Add(threadNum)
|
||||
sess.Out.Debug("Threads for repository gathering: %d\n", threadNum)
|
||||
for i := 0; i < threadNum; i++ {
|
||||
go func() {
|
||||
for {
|
||||
target, ok := <-ch
|
||||
if !ok {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
repos, err := core.GetRepositoriesFromOwner(target.Login, sess.GithubClient)
|
||||
if err != nil {
|
||||
sess.Out.Error(" Failed to retrieve repositories from %s: %s\n", *target.Login, err)
|
||||
}
|
||||
if len(repos) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, repo := range repos {
|
||||
sess.Out.Debug(" Retrieved repository: %s\n", *repo.FullName)
|
||||
sess.AddRepository(repo)
|
||||
}
|
||||
sess.Stats.IncrementTargets()
|
||||
sess.Out.Info(" Retrieved %d %s from %s\n", len(repos), core.Pluralize(len(repos), "repository", "repositories"), *target.Login)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for _, target := range sess.Targets {
|
||||
ch <- target
|
||||
}
|
||||
close(ch)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func AnalyzeRepositories(sess *core.Session) {
|
||||
sess.Stats.Status = core.StatusAnalyzing
|
||||
var ch = make(chan *core.GithubRepository, len(sess.Repositories))
|
||||
var wg sync.WaitGroup
|
||||
var threadNum int
|
||||
if len(sess.Repositories) == 1 {
|
||||
threadNum = 1
|
||||
} else if len(sess.Repositories) <= *sess.Options.Threads {
|
||||
threadNum = len(sess.Repositories) - 1
|
||||
} else {
|
||||
threadNum = *sess.Options.Threads
|
||||
}
|
||||
wg.Add(threadNum)
|
||||
sess.Out.Debug("Threads for repository analysis: %d\n", threadNum)
|
||||
|
||||
sess.Out.Important("Analyzing %d %s...\n", len(sess.Repositories), core.Pluralize(len(sess.Repositories), "repository", "repositories"))
|
||||
|
||||
for i := 0; i < threadNum; i++ {
|
||||
go func(tid int) {
|
||||
for {
|
||||
sess.Out.Debug("[THREAD #%d] Requesting new repository to analyze...\n", tid)
|
||||
repo, ok := <-ch
|
||||
if !ok {
|
||||
sess.Out.Debug("[THREAD #%d] No more tasks, marking WaitGroup as done\n", tid)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
sess.Out.Debug("[THREAD #%d][%s] Cloning repository...\n", tid, *repo.FullName)
|
||||
clone, path, err := core.CloneRepository(repo.CloneURL, repo.DefaultBranch, *sess.Options.CommitDepth)
|
||||
if err != nil {
|
||||
if err.Error() != "remote repository is empty" {
|
||||
sess.Out.Error("Error cloning repository %s: %s\n", *repo.FullName, err)
|
||||
}
|
||||
sess.Stats.IncrementRepositories()
|
||||
sess.Stats.UpdateProgress(sess.Stats.Repositories, len(sess.Repositories))
|
||||
continue
|
||||
}
|
||||
sess.Out.Debug("[THREAD #%d][%s] Cloned repository to: %s\n", tid, *repo.FullName, path)
|
||||
|
||||
history, err := core.GetRepositoryHistory(clone)
|
||||
if err != nil {
|
||||
sess.Out.Error("[THREAD #%d][%s] Error getting commit history: %s\n", tid, *repo.FullName, err)
|
||||
os.RemoveAll(path)
|
||||
sess.Stats.IncrementRepositories()
|
||||
sess.Stats.UpdateProgress(sess.Stats.Repositories, len(sess.Repositories))
|
||||
continue
|
||||
}
|
||||
sess.Out.Debug("[THREAD #%d][%s] Number of commits: %d\n", tid, *repo.FullName, len(history))
|
||||
|
||||
for _, commit := range history {
|
||||
sess.Out.Debug("[THREAD #%d][%s] Analyzing commit: %s\n", tid, *repo.FullName, commit.Hash)
|
||||
changes, _ := core.GetChanges(commit, clone)
|
||||
sess.Out.Debug("[THREAD #%d][%s] Changes in %s: %d\n", tid, *repo.FullName, commit.Hash, len(changes))
|
||||
for _, change := range changes {
|
||||
changeAction := core.GetChangeAction(change)
|
||||
path := core.GetChangePath(change)
|
||||
matchFile := core.NewMatchFile(path)
|
||||
sess.Out.Debug("[THREAD #%d][%s] Matching: %s...\n", tid, *repo.FullName, matchFile.Path)
|
||||
for _, signature := range core.Signatures {
|
||||
if signature.Match(matchFile) {
|
||||
|
||||
finding := &core.Finding{
|
||||
FilePath: path,
|
||||
Action: changeAction,
|
||||
Description: signature.Description(),
|
||||
Comment: signature.Comment(),
|
||||
RepositoryOwner: *repo.Owner,
|
||||
RepositoryName: *repo.Name,
|
||||
CommitHash: commit.Hash.String(),
|
||||
CommitMessage: strings.TrimSpace(commit.Message),
|
||||
CommitAuthor: commit.Author.String(),
|
||||
}
|
||||
finding.Initialize()
|
||||
sess.AddFinding(finding)
|
||||
|
||||
sess.Out.Warn(" %s: %s\n", strings.ToUpper(changeAction), finding.Description)
|
||||
sess.Out.Info(" Path.......: %s\n", finding.FilePath)
|
||||
sess.Out.Info(" Repo.......: %s\n", *repo.FullName)
|
||||
sess.Out.Info(" Message....: %s\n", core.TruncateString(finding.CommitMessage, 100))
|
||||
sess.Out.Info(" Author.....: %s\n", finding.CommitAuthor)
|
||||
if finding.Comment != "" {
|
||||
sess.Out.Info(" Comment....: %s\n", finding.Comment)
|
||||
}
|
||||
sess.Out.Info(" File URL...: %s\n", finding.FileUrl)
|
||||
sess.Out.Info(" Commit URL.: %s\n", finding.CommitUrl)
|
||||
sess.Out.Info(" ------------------------------------------------\n\n")
|
||||
sess.Stats.IncrementFindings()
|
||||
break
|
||||
}
|
||||
}
|
||||
sess.Stats.IncrementFiles()
|
||||
}
|
||||
sess.Stats.IncrementCommits()
|
||||
sess.Out.Debug("[THREAD #%d][%s] Done analyzing changes in %s\n", tid, *repo.FullName, commit.Hash)
|
||||
}
|
||||
sess.Out.Debug("[THREAD #%d][%s] Done analyzing commits\n", tid, *repo.FullName)
|
||||
os.RemoveAll(path)
|
||||
sess.Out.Debug("[THREAD #%d][%s] Deleted %s\n", tid, *repo.FullName, path)
|
||||
sess.Stats.IncrementRepositories()
|
||||
sess.Stats.UpdateProgress(sess.Stats.Repositories, len(sess.Repositories))
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
for _, repo := range sess.Repositories {
|
||||
ch <- repo
|
||||
}
|
||||
close(ch)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func PrintSessionStats(sess *core.Session) {
|
||||
sess.Out.Info("\nFindings....: %d\n", sess.Stats.Findings)
|
||||
sess.Out.Info("Files.......: %d\n", sess.Stats.Files)
|
||||
sess.Out.Info("Commits.....: %d\n", sess.Stats.Commits)
|
||||
sess.Out.Info("Repositories: %d\n", sess.Stats.Repositories)
|
||||
sess.Out.Info("Targets.....: %d\n\n", sess.Stats.Targets)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if sess, err = core.NewSession(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sess.Out.Info("%s\n\n", core.ASCIIBanner)
|
||||
sess.Out.Important("%s v%s started at %s\n", core.Name, core.Version, sess.Stats.StartedAt.Format(time.RFC3339))
|
||||
sess.Out.Important("Loaded %d signatures\n", len(core.Signatures))
|
||||
sess.Out.Important("Web interface available at http://%s:%d\n", *sess.Options.BindAddress, *sess.Options.Port)
|
||||
|
||||
if sess.Stats.Status == "finished" {
|
||||
sess.Out.Important("Loaded session file: %s\n", *sess.Options.Load)
|
||||
} else {
|
||||
if len(sess.Options.Logins) == 0 {
|
||||
sess.Out.Fatal("Please provide at least one GitHub organization or user\n")
|
||||
}
|
||||
|
||||
GatherTargets(sess)
|
||||
GatherRepositories(sess)
|
||||
AnalyzeRepositories(sess)
|
||||
sess.Finish()
|
||||
|
||||
if *sess.Options.Save != "" {
|
||||
err := sess.SaveToFile(*sess.Options.Save)
|
||||
if err != nil {
|
||||
sess.Out.Error("Error saving session to %s: %s\n", *sess.Options.Save, err)
|
||||
}
|
||||
sess.Out.Important("Saved session to: %s\n\n", *sess.Options.Save)
|
||||
}
|
||||
}
|
||||
|
||||
PrintSessionStats(sess)
|
||||
sess.Out.Important("Press Ctrl+C to stop web server and exit.\n\n")
|
||||
select {}
|
||||
}
|
||||
30
release.sh
Executable file
30
release.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
CURRENT_VERSION=$(cat core/banner.go | grep Version | cut -d '"' -f 2)
|
||||
TO_UPDATE=(
|
||||
core/banner.go
|
||||
)
|
||||
|
||||
read -p "[?] Did you remember to update CHANGELOG.md? "
|
||||
read -p "[?] Did you remember to update README.md with new features/changes? "
|
||||
|
||||
echo -n "[*] Current version is $CURRENT_VERSION. Enter new version: "
|
||||
read NEW_VERSION
|
||||
echo "[*] Pushing and tagging version $NEW_VERSION in 5 seconds..."
|
||||
sleep 5
|
||||
|
||||
for file in "${TO_UPDATE[@]}"
|
||||
do
|
||||
echo "[*] Patching $file ..."
|
||||
sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" $file
|
||||
git add $file
|
||||
done
|
||||
|
||||
git commit -m "Releasing v$NEW_VERSION"
|
||||
git push
|
||||
|
||||
git tag -a v$NEW_VERSION -m "Release v$NEW_VERSION"
|
||||
git push origin v$NEW_VERSION
|
||||
|
||||
echo
|
||||
echo "[*] All done, v$NEW_VERSION released."
|
||||
BIN
static/fonts/open-iconic.eot
Executable file
BIN
static/fonts/open-iconic.eot
Executable file
Binary file not shown.
BIN
static/fonts/open-iconic.otf
Executable file
BIN
static/fonts/open-iconic.otf
Executable file
Binary file not shown.
543
static/fonts/open-iconic.svg
Executable file
543
static/fonts/open-iconic.svg
Executable file
@@ -0,0 +1,543 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2014-7-1: Created.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
|
||||
By P.J. Onori
|
||||
Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="open-iconic" horiz-adv-x="800" >
|
||||
<font-face
|
||||
font-family="Icons"
|
||||
font-weight="400"
|
||||
font-stretch="normal"
|
||||
units-per-em="800"
|
||||
panose-1="2 0 5 3 0 0 0 0 0 0"
|
||||
ascent="800"
|
||||
descent="0"
|
||||
bbox="-0.5 -101 802 800.126"
|
||||
underline-thickness="50"
|
||||
underline-position="-100"
|
||||
unicode-range="U+E000-E0DE"
|
||||
/>
|
||||
<missing-glyph />
|
||||
<glyph glyph-name="" unicode=""
|
||||
d="M300 700h500v-700h-500v100h400v500h-400v100zM400 500l200 -150l-200 -150v100h-400v100h400v100z" />
|
||||
<glyph glyph-name="1" unicode=""
|
||||
d="M300 700h500v-700h-500v100h400v500h-400v100zM200 500v-100h400v-100h-400v-100l-200 150z" />
|
||||
<glyph glyph-name="2" unicode=""
|
||||
d="M350 700c193 0 350 -157 350 -350v-50h100l-200 -200l-200 200h100v50c0 138 -112 250 -250 250s-250 -112 -250 -250c0 193 157 350 350 350z" />
|
||||
<glyph glyph-name="3" unicode=""
|
||||
d="M450 700c193 0 350 -157 350 -350c0 138 -112 250 -250 250s-250 -112 -250 -250v-50h100l-200 -200l-200 200h100v50c0 193 157 350 350 350z" />
|
||||
<glyph glyph-name="4" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM100 500h600v-100h-600v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="5" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h600v-100h-600v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="6" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM200 500h600v-100h-600v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="7" unicode=""
|
||||
d="M400 700c75 0 146 -23 206 -59l-75 -225l-322 234c57 31 122 50 191 50zM125 588l191 -138l-310 -222c-4 24 -6 47 -6 72c0 114 49 215 125 288zM688 575c69 -72 112 -168 112 -275c0 -35 -8 -68 -16 -100h-218zM216 253l112 -347c-128 23 -232 109 -287 222zM372 100
|
||||
h372c-64 -109 -177 -185 -310 -197z" />
|
||||
<glyph glyph-name="8" unicode="" horiz-adv-x="600"
|
||||
d="M200 800h100v-500h200l-247 -300l-253 300h200v500z" />
|
||||
<glyph glyph-name="9" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 700v-300h-200l300 -300l300 300h-200v300h-200z" />
|
||||
<glyph glyph-name="a" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300l300 -300v200h300v200h-300v200z" />
|
||||
<glyph glyph-name="b" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700v-200h-300v-200h300v-200l300 300z" />
|
||||
<glyph glyph-name="c" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300h200v-300h200v300h200z" />
|
||||
<glyph glyph-name="d" unicode=""
|
||||
d="M300 600v-200h500v-100h-500v-200l-300 247z" />
|
||||
<glyph glyph-name="e" unicode=""
|
||||
d="M500 600l300 -247l-300 -253v200h-500v100h500v200z" />
|
||||
<glyph glyph-name="f" unicode="" horiz-adv-x="600"
|
||||
d="M200 800h200v-500h200l-297 -300l-303 300h200v500z" />
|
||||
<glyph glyph-name="10" unicode=""
|
||||
d="M300 700v-200h500v-200h-500v-200l-300 297z" />
|
||||
<glyph glyph-name="11" unicode=""
|
||||
d="M500 700l300 -297l-300 -303v200h-500v200h500v200z" />
|
||||
<glyph glyph-name="12" unicode="" horiz-adv-x="600"
|
||||
d="M297 800l303 -300h-200v-500h-200v500h-200z" />
|
||||
<glyph glyph-name="13" unicode="" horiz-adv-x="600"
|
||||
d="M247 800l253 -300h-200v-500h-100v500h-200z" />
|
||||
<glyph glyph-name="14" unicode=""
|
||||
d="M400 800h100v-800h-100v800zM200 700h100v-600h-100v600zM600 600h100v-400h-100v400zM0 500h100v-200h-100v200z" />
|
||||
<glyph glyph-name="15" unicode=""
|
||||
d="M116 600l72 -72c-54 -54 -88 -126 -88 -209s34 -159 88 -213l-72 -72c-72 72 -116 175 -116 285s44 209 116 281zM684 600c72 -72 116 -171 116 -281s-44 -213 -116 -285l-72 72c54 54 88 130 88 213s-34 155 -88 209zM259 460l69 -72c-18 -18 -28 -41 -28 -69
|
||||
s10 -54 28 -72l-69 -72c-36 36 -59 89 -59 144s23 105 59 141zM541 459c36 -36 59 -85 59 -140s-23 -108 -59 -144l-69 72c18 18 28 44 28 72s-10 51 -28 69z" />
|
||||
<glyph glyph-name="16" unicode="" horiz-adv-x="400"
|
||||
d="M200 800c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM100 319c31 -11 65 -19 100 -19s68 8 100 19v-319l-100 100l-100 -100v319z" />
|
||||
<glyph glyph-name="17" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300c0 -66 21 -126 56 -175l419 419c-49 35 -109 56 -175 56zM644 575l-419 -419c49 -35 109 -56 175 -56c166 0 300 134 300 300
|
||||
c0 66 -21 126 -56 175z" />
|
||||
<glyph glyph-name="18" unicode=""
|
||||
d="M0 700h100v-600h700v-100h-800v700zM500 700h200v-500h-200v500zM200 500h200v-300h-200v300z" />
|
||||
<glyph glyph-name="19" unicode=""
|
||||
d="M397 800c13 1 23 -4 34 -13c2 -2 214 -254 241 -287h128v-100h-100v-366c0 -18 -16 -34 -34 -34h-532c-18 0 -34 16 -34 34v366h-100v100h128l234 281c9 11 22 18 35 19zM400 672l-144 -172h288zM250 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50
|
||||
v100c0 28 -22 50 -50 50zM550 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50v100c0 28 -22 50 -50 50z" />
|
||||
<glyph glyph-name="1a" unicode=""
|
||||
d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9zM100 600v-400h500v400h-500z" />
|
||||
<glyph glyph-name="1b" unicode=""
|
||||
d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9z" />
|
||||
<glyph glyph-name="1c" unicode=""
|
||||
d="M92 650c0 23 19 50 45 50h3h5h5h500c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-141c9 -17 120 -231 166 -309c16 -26 34 -61 34 -106c0 -39 -15 -77 -41 -103h-3c-26 -25 -62 -41 -100 -41h-512c-39 0 -77 15 -103 41s-41 64 -41 103c0 46 18 80 34 106
|
||||
c46 78 157 292 166 309v141h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51zM500 600h-200v-162l-6 -10s-63 -123 -119 -228h450c-56 105 -119 228 -119 228l-6 10v162z" />
|
||||
<glyph glyph-name="1d" unicode=""
|
||||
d="M400 800c110 0 200 -90 200 -200c0 -104 52 -198 134 -266c41 -34 66 -82 66 -134h-800c0 52 25 100 66 134c82 68 134 162 134 266c0 110 90 200 200 200zM300 100h200c0 -55 -45 -100 -100 -100s-100 45 -100 100z" />
|
||||
<glyph glyph-name="1e" unicode="" horiz-adv-x="600"
|
||||
d="M150 800h50l350 -250l-225 -147l225 -153l-350 -250h-50v250l-75 -75l-75 75l150 150l-150 150l75 75l75 -75v250zM250 650v-200l150 100zM250 350v-200l150 100z" />
|
||||
<glyph glyph-name="1f" unicode=""
|
||||
d="M0 800h500c110 0 200 -90 200 -200c0 -47 -17 -91 -44 -125c85 -40 144 -125 144 -225c0 -138 -112 -250 -250 -250h-550v100c55 0 100 45 100 100v400c0 55 -45 100 -100 100v100zM300 700v-200h100c55 0 100 45 100 100s-45 100 -100 100h-100zM300 400v-300h150
|
||||
c83 0 150 67 150 150s-67 150 -150 150h-150z" />
|
||||
<glyph glyph-name="20" unicode="" horiz-adv-x="600"
|
||||
d="M300 800v-300h200l-300 -500v300h-200z" />
|
||||
<glyph glyph-name="21" unicode=""
|
||||
d="M100 800h300v-300l100 100l100 -100v300h50c28 0 50 -22 50 -50v-550h-550c-28 0 -50 -22 -50 -50s22 -50 50 -50h550v-100h-550c-83 0 -150 67 -150 150v550l3 19c8 39 39 70 78 78z" />
|
||||
<glyph glyph-name="22" unicode="" horiz-adv-x="400"
|
||||
d="M0 800h400v-800l-200 200l-200 -200v800z" />
|
||||
<glyph glyph-name="23" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM0 600h300v-103h203v103h297v-591c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v591z" />
|
||||
<glyph glyph-name="24" unicode=""
|
||||
d="M300 800h200c55 0 100 -45 100 -100v-100h191c6 0 9 -3 9 -9v-241c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v241c0 6 3 9 9 9h191v100c0 55 45 100 100 100zM300 700v-100h200v100h-200zM0 209c16 -6 32 -9 50 -9h700c18 0 34 3 50 9v-200c0 -6 -3 -9 -9 -9h-782
|
||||
c-6 0 -9 3 -9 9v200z" />
|
||||
<glyph glyph-name="25" unicode="" horiz-adv-x="600"
|
||||
d="M300 800c58 0 110 -16 147 -53s53 -89 53 -147h-100c0 39 -11 61 -25 75s-36 25 -75 25c-35 0 -55 -10 -72 -31s-28 -55 -28 -94c0 -51 20 -107 28 -175h172v-100h-178c-14 -60 -49 -127 -113 -200h491v-100h-600v122l16 12c69 69 95 121 106 166h-122v100h125
|
||||
c-8 50 -25 106 -25 175c0 58 16 114 50 156c34 43 88 69 150 69z" />
|
||||
<glyph glyph-name="26" unicode=""
|
||||
d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-700c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v700v2c0 20 15 42 34 48zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50zM350 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h300c28 0 50 22 50 50
|
||||
s-22 50 -50 50h-300zM100 400v-400h600v400h-600z" />
|
||||
<glyph glyph-name="27" unicode=""
|
||||
d="M744 797l6 -3l44 -44c4 -4 3 -8 0 -12l-266 -375l-15 -13l-25 -12c-23 72 -78 127 -150 150l12 25l13 15l375 266zM266 400c74 0 134 -60 134 -134c0 -147 -119 -266 -266 -266c-48 0 -95 12 -134 34c80 46 134 133 134 232c0 74 58 134 132 134z" />
|
||||
<glyph glyph-name="28" unicode=""
|
||||
d="M9 451c0 23 19 50 46 50c8 0 19 -3 26 -7l131 -66l29 22c-79 81 -1 250 118 250s197 -167 119 -250l28 -22l131 66c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-115 -56c9 -16 19 -33 25 -50h68c28 0 50 -22 50 -50s-22 -50 -50 -50h-50
|
||||
c0 -23 -2 -45 -6 -66l78 -40c21 -5 37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11l-65 35c-24 -46 -62 -86 -103 -110c-35 19 -60 45 -60 72v135v4v5v6v5v5v87c0 28 -22 50 -50 50c-24 0 -45 -17 -50 -40c1 -3 1 -8 1 -11s0 -8 -1 -11v-82v-4v-5v-144
|
||||
c0 -28 -24 -53 -59 -72c-41 25 -79 64 -103 110l-66 -35c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49l78 40c-4 21 -6 43 -6 66h-50h-5c-28 0 -50 22 -50 50c0 26 22 50 50 50h5h69c6 17 16 34 25 50l-116 56c-16 7 -28 27 -28 45z" />
|
||||
<glyph glyph-name="29" unicode=""
|
||||
d="M600 700h91c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-91v600zM210 503l290 147v-500l-250 125v-3c-15 0 -25 -8 -28 -22l75 -178c11 -25 0 -58 -25 -69s-58 0 -69 25l-103 272h-91c-6 0 -9 3 -9 9v182c0 6 3 9 9 9h182z" />
|
||||
<glyph glyph-name="2a" unicode=""
|
||||
d="M9 800h682c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM100 700v-200h500v200h-500zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-300h100v300h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
|
||||
<glyph glyph-name="2b" unicode=""
|
||||
d="M0 800h700v-200h-700v200zM0 500h700v-491c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v491zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
|
||||
<glyph glyph-name="2c" unicode=""
|
||||
d="M409 800h182c6 0 10 -4 12 -9l94 -182c2 -5 6 -9 12 -9h82c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v441c0 83 67 150 150 150h141c6 0 10 4 12 9l94 182c2 5 6 9 12 9zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z
|
||||
M500 500c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM500 400c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="2d" unicode=""
|
||||
d="M0 600h800l-400 -400z" />
|
||||
<glyph glyph-name="2e" unicode="" horiz-adv-x="400"
|
||||
d="M400 800v-800l-400 400z" />
|
||||
<glyph glyph-name="2f" unicode="" horiz-adv-x="400"
|
||||
d="M0 800l400 -400l-400 -400v800z" />
|
||||
<glyph glyph-name="30" unicode=""
|
||||
d="M400 600l400 -400h-800z" />
|
||||
<glyph glyph-name="31" unicode=""
|
||||
d="M0 550c0 23 20 50 46 50h3h5h4h200c17 0 37 -13 44 -28l38 -72h444c14 0 19 -12 15 -25l-81 -250c-4 -13 -21 -25 -35 -25h-350c-14 0 -30 12 -34 25c-27 83 -54 167 -81 250l-10 25h-150c-2 0 -5 -1 -7 -1c-28 0 -51 23 -51 51zM358 100c28 0 50 -22 50 -50
|
||||
s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM658 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="32" unicode=""
|
||||
d="M0 700h500v-100h-300v-300h-100l-100 -100v500zM300 500h500v-500l-100 100h-400v400z" />
|
||||
<glyph glyph-name="33" unicode=""
|
||||
d="M641 700l143 -141l-493 -493c-71 76 -146 148 -219 222l-72 71l141 141c50 -51 101 -101 153 -150c116 117 234 231 347 350z" />
|
||||
<glyph glyph-name="34" unicode=""
|
||||
d="M150 600l250 -250l250 250l150 -150l-400 -400l-400 400z" />
|
||||
<glyph glyph-name="35" unicode="" horiz-adv-x="600"
|
||||
d="M400 800l150 -150l-250 -250l250 -250l-150 -150l-400 400z" />
|
||||
<glyph glyph-name="36" unicode="" horiz-adv-x="600"
|
||||
d="M150 800l400 -400l-400 -400l-150 150l250 250l-250 250z" />
|
||||
<glyph glyph-name="37" unicode=""
|
||||
d="M400 600l400 -400l-150 -150l-250 250l-250 -250l-150 150z" />
|
||||
<glyph glyph-name="38" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM600 622l-250 -250l-100 100l-72 -72l172 -172l322 322z" />
|
||||
<glyph glyph-name="39" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM250 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
|
||||
<glyph glyph-name="3a" unicode=""
|
||||
d="M350 800c28 0 50 -22 50 -50v-50h75c14 0 25 -11 25 -25v-75h-300v75c0 14 11 25 25 25h75v50c0 28 22 50 50 50zM25 700h75v-200h500v200h75c14 0 25 -11 25 -25v-650c0 -14 -11 -25 -25 -25h-650c-14 0 -25 11 -25 25v650c0 14 11 25 25 25z" />
|
||||
<glyph glyph-name="3b" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM350 600h100v-181c23 -24 47 -47 72 -69l-72 -72c-27 30 -55 59 -84 88l-16 12
|
||||
v222z" />
|
||||
<glyph glyph-name="3c" unicode=""
|
||||
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-191v50c0 83 -67 150 -150 150s-150 -67 -150 -150v-50h-272c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM434 400h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1
|
||||
v-150h150l-200 -200l-200 200h150v150v2c0 20 15 42 34 48z" />
|
||||
<glyph glyph-name="3d" unicode=""
|
||||
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -3 -34 -9 -50h-141l-200 200l-200 -200h-222c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM450 350l250 -250h-200v-50c0 -28 -22 -50 -50 -50s-50 22 -50 50v50h-200z" />
|
||||
<glyph glyph-name="3e" unicode=""
|
||||
d="M450 700c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200s90 200 200 200c23 114 129 200 250 200z" />
|
||||
<glyph glyph-name="3f" unicode=""
|
||||
d="M250 800c82 0 154 -40 200 -100c-143 0 -270 -85 -325 -209c-36 -10 -70 -25 -100 -47c-16 33 -25 67 -25 106c0 138 112 250 250 250zM450 600c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200
|
||||
s90 200 200 200c23 114 129 200 250 200z" />
|
||||
<glyph glyph-name="40" unicode=""
|
||||
d="M500 700h100l-300 -600h-100zM100 600h100l-100 -200l100 -200h-100l-100 200zM600 600h100l100 -200l-100 -200h-100l100 200z" />
|
||||
<glyph glyph-name="41" unicode=""
|
||||
d="M350 800h100l50 -119l28 -12l119 50l72 -72l-50 -119l12 -28l119 -50v-100l-119 -50l-12 -28l50 -119l-72 -72l-119 50l-28 -12l-50 -119h-100l-50 119l-28 12l-119 -50l-72 72l50 119l-12 28l-119 50v100l119 50l12 28l-50 119l72 72l119 -50l28 12zM400 550
|
||||
c-83 0 -150 -67 -150 -150s67 -150 150 -150s150 67 150 150s-67 150 -150 150z" />
|
||||
<glyph glyph-name="42" unicode=""
|
||||
d="M0 800h800v-200h-800v200zM200 500h400l-200 -200zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="43" unicode=""
|
||||
d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM500 600v-400l-200 200z" />
|
||||
<glyph glyph-name="44" unicode=""
|
||||
d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM300 600l200 -200l-200 -200v400z" />
|
||||
<glyph glyph-name="45" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM400 500l200 -200h-400zM0 200h800v-200h-800v200z" />
|
||||
<glyph glyph-name="46" unicode=""
|
||||
d="M150 700c83 0 150 -67 150 -150v-50h100v50c0 83 67 150 150 150s150 -67 150 -150s-67 -150 -150 -150h-50v-100h50c83 0 150 -67 150 -150s-67 -150 -150 -150s-150 67 -150 150v50h-100v-50c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150h50v100h-50
|
||||
c-83 0 -150 67 -150 150s67 150 150 150zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h50v50c0 28 -22 50 -50 50zM550 600c-28 0 -50 -22 -50 -50v-50h50c28 0 50 22 50 50s-22 50 -50 50zM300 400v-100h100v100h-100zM150 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
|
||||
s50 22 50 50v50h-50zM500 200v-50c0 -28 22 -50 50 -50s50 22 50 50s-22 50 -50 50h-50z" />
|
||||
<glyph glyph-name="47" unicode=""
|
||||
d="M0 791c0 5 4 9 9 9h782c6 0 9 -4 9 -10v-790l-200 200h-591c-6 0 -9 3 -9 9v582z" />
|
||||
<glyph glyph-name="48" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM600 600l-100 -300l-300 -100l100 300zM400 450c-28 0 -50 -22 -50 -50
|
||||
s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="49" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700v-600c166 0 300 134 300 300s-134 300 -300 300z" />
|
||||
<glyph glyph-name="4a" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM0 600h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100zM750 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="4b" unicode=""
|
||||
d="M25 700h750c14 0 25 -11 25 -25v-75h-800v75c0 14 11 25 25 25zM0 500h800v-375c0 -14 -11 -25 -25 -25h-750c-14 0 -25 11 -25 25v375zM100 300v-100h100v100h-100zM300 300v-100h100v100h-100z" />
|
||||
<glyph glyph-name="4c" unicode=""
|
||||
d="M100 800h100v-100h450l100 100l50 -50l-100 -100v-450h100v-100h-100v-100h-100v100h-500v500h-100v100h100v100zM200 600v-350l350 350h-350zM600 550l-350 -350h350v350z" />
|
||||
<glyph glyph-name="4d" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z
|
||||
M200 452c0 20 15 42 34 48h3h3h8c12 0 28 -7 36 -16l91 -90l25 6c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100l6 25l-90 91c-9 8 -16 24 -16 36zM550 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="4e" unicode=""
|
||||
d="M300 800h200v-300h200l-300 -300l-300 300h200v300zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="4f" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM400 600l300 -300h-200v-300h-200v300h-200z" />
|
||||
<glyph glyph-name="50" unicode=""
|
||||
d="M200 700h600v-600h-600l-200 300zM350 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
|
||||
<glyph glyph-name="51" unicode=""
|
||||
d="M400 700c220 0 400 -180 400 -400h-100c0 166 -134 300 -300 300s-300 -134 -300 -300h-100c0 220 180 400 400 400zM341 491l59 -88l59 88c81 -25 141 -101 141 -191c0 -110 -90 -200 -200 -200s-200 90 -200 200c0 90 60 166 141 191z" />
|
||||
<glyph glyph-name="52" unicode=""
|
||||
d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300zM100 600v-100h100v100h-100zM100 400v-100h100v100h-100zM100 200v-100h400v100h-400z" />
|
||||
<glyph glyph-name="53" unicode="" horiz-adv-x="600"
|
||||
d="M200 700h100v-100h75c30 0 58 -6 81 -22s44 -44 44 -78v-100h-100v94c-4 3 -13 6 -25 6h-250c-14 0 -25 -11 -25 -25v-50c0 -15 20 -40 34 -44l257 -65c66 -16 109 -73 109 -141v-50c0 -68 -57 -125 -125 -125h-75v-100h-100v100h-75c-30 0 -58 6 -81 22s-44 44 -44 78
|
||||
v100h100v-94c4 -3 13 -6 25 -6h250c14 0 25 11 25 25v50c0 15 -20 40 -34 44l-257 65c-66 16 -109 73 -109 141v50c0 68 57 125 125 125h75v100z" />
|
||||
<glyph glyph-name="54" unicode=""
|
||||
d="M0 700h300v-300l-300 -300v600zM500 700h300v-300l-300 -300v600z" />
|
||||
<glyph glyph-name="55" unicode=""
|
||||
d="M300 700v-600h-300v300zM800 700v-600h-300v300z" />
|
||||
<glyph glyph-name="56" unicode=""
|
||||
d="M300 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300zM800 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300z" />
|
||||
<glyph glyph-name="57" unicode=""
|
||||
d="M0 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300zM500 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300z" />
|
||||
<glyph glyph-name="58" unicode="" horiz-adv-x="600"
|
||||
d="M300 800l34 -34c11 -11 266 -270 266 -488c0 -165 -135 -300 -300 -300s-300 135 -300 300c0 218 255 477 266 488zM150 328c-28 0 -50 -22 -50 -50c0 -110 90 -200 200 -200c28 0 50 22 50 50s-22 50 -50 50c-55 0 -100 45 -100 100c0 28 -22 50 -50 50z" />
|
||||
<glyph glyph-name="59" unicode=""
|
||||
d="M400 800l400 -500h-800zM0 200h800v-200h-800v200z" />
|
||||
<glyph glyph-name="5a" unicode="" horiz-adv-x="600"
|
||||
d="M300 800l300 -300h-600zM0 300h600l-300 -300z" />
|
||||
<glyph glyph-name="5b" unicode=""
|
||||
d="M0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200z" />
|
||||
<glyph glyph-name="5c" unicode=""
|
||||
d="M0 700h800v-100l-400 -200l-400 200v100zM0 500l400 -200l400 200v-400h-800v400z" />
|
||||
<glyph glyph-name="5d" unicode=""
|
||||
d="M400 800l400 -200v-600h-800v600zM400 688l-300 -150v-188l300 -150l300 150v188zM200 500h400v-100l-200 -100l-200 100v100z" />
|
||||
<glyph glyph-name="5e" unicode=""
|
||||
d="M600 700c69 0 134 -19 191 -50l-16 -106c-49 35 -109 56 -175 56c-131 0 -240 -84 -281 -200h331l-16 -100h-334c0 -36 8 -68 19 -100h297l-16 -100h-222c55 -61 133 -100 222 -100c78 0 147 30 200 78v-122c-59 -35 -127 -56 -200 -56c-147 0 -274 82 -344 200h-256
|
||||
l19 100h197c-8 32 -16 66 -16 100h-200l25 100h191c45 172 198 300 384 300z" />
|
||||
<glyph glyph-name="5f" unicode=""
|
||||
d="M0 700h700v-100h-700v100zM0 500h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100z" />
|
||||
<glyph glyph-name="60" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM200 600h400l-200 -200zM0 200h800v-200h-800v200z" />
|
||||
<glyph glyph-name="61" unicode=""
|
||||
d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM200 600l200 -200l-200 -200v400z" />
|
||||
<glyph glyph-name="62" unicode=""
|
||||
d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM600 600v-400l-200 200z" />
|
||||
<glyph glyph-name="63" unicode=""
|
||||
d="M0 800h800v-200h-800v200zM400 400l200 -200h-400zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="64" unicode=""
|
||||
d="M0 800h200v-100h-100v-600h600v100h100v-200h-800v800zM400 800h400v-400l-150 150l-250 -250l-100 100l250 250z" />
|
||||
<glyph glyph-name="65" unicode=""
|
||||
d="M403 700c247 0 397 -300 397 -300s-150 -300 -397 -300c-253 0 -403 300 -403 300s150 300 403 300zM400 600c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM400 500c10 0 19 -3 28 -6c-16 -8 -28 -24 -28 -44c0 -28 22 -50 50 -50
|
||||
c20 0 36 12 44 28c3 -9 6 -18 6 -28c0 -55 -45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="66" unicode="" horiz-adv-x="900"
|
||||
d="M331 700h3h3c3 1 7 1 10 1c12 0 29 -8 37 -17l94 -93l66 65c57 57 155 57 212 0c58 -58 58 -154 0 -212l-65 -66l93 -94c10 -8 18 -25 18 -38c0 -28 -22 -50 -50 -50c-13 0 -32 9 -40 20l-62 65l-381 -381h-269v272l375 381l-63 63c-9 8 -16 24 -16 36c0 20 16 42 35 48z
|
||||
M447 481l-313 -315l128 -132l316 316z" />
|
||||
<glyph glyph-name="67" unicode=""
|
||||
d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300z" />
|
||||
<glyph glyph-name="68" unicode=""
|
||||
d="M200 800c0 0 200 -100 200 -300s-298 -302 -200 -500c0 0 -200 100 -200 300s300 300 200 500zM500 500c0 0 200 -100 200 -300c0 -150 -60 -200 -100 -200h-300c0 200 300 300 200 500z" />
|
||||
<glyph glyph-name="69" unicode=""
|
||||
d="M0 800h100v-800h-100v800zM200 800h300v-100h300l-200 -203l200 -197h-400v100h-200v400z" />
|
||||
<glyph glyph-name="6a" unicode="" horiz-adv-x="400"
|
||||
d="M150 800h150l-100 -200h200l-150 -300h150l-300 -300l-100 300h134l66 200h-200z" />
|
||||
<glyph glyph-name="6b" unicode=""
|
||||
d="M0 800h300v-100h500v-100h-800v200zM0 500h800v-450c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v450z" />
|
||||
<glyph glyph-name="6c" unicode=""
|
||||
d="M150 800c83 0 150 -67 150 -150c0 -66 -41 -121 -100 -141v-118c15 5 33 9 50 9h200c28 0 50 22 50 50v59c-59 20 -100 75 -100 141c0 83 67 150 150 150s150 -67 150 -150c0 -66 -41 -121 -100 -141v-59c0 -82 -68 -150 -150 -150h-200c-14 0 -25 -7 -34 -16
|
||||
c50 -24 84 -74 84 -134c0 -83 -67 -150 -150 -150s-150 67 -150 150c0 66 41 121 100 141v218c-59 20 -100 75 -100 141c0 83 67 150 150 150z" />
|
||||
<glyph glyph-name="6d" unicode=""
|
||||
d="M0 800h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400zM500 400l150 -150l150 150v-400h-400l150 150l-150 150z" />
|
||||
<glyph glyph-name="6e" unicode=""
|
||||
d="M100 800l150 -150l150 150v-400h-400l150 150l-150 150zM400 400h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400z" />
|
||||
<glyph glyph-name="6f" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700c-56 0 -108 -17 -153 -44l22 -19c33 -18 13 -48 -13 -59c-30 -13 -77 10 -65 -41c13 -55 -27 -3 -47 -15c-42 -26 49 -152 31 -156l-59 34c-8 0 -13 -5 -16 -10
|
||||
c1 -30 10 -57 19 -84c28 -11 77 -2 100 -25c47 -28 97 -115 75 -159c34 -13 68 -22 106 -22c101 0 193 48 247 125c3 24 -8 44 -50 44c-69 0 -156 13 -153 97c2 46 101 108 66 143c-30 30 12 39 12 66c0 37 -65 32 -69 50s20 36 41 56c-30 10 -60 19 -94 19zM631 591
|
||||
c-38 -11 -94 -35 -87 -53c6 -15 52 -1 65 -13c11 -10 16 -59 44 -31l22 22v3c-11 26 -26 50 -44 72z" />
|
||||
<glyph glyph-name="70" unicode=""
|
||||
d="M703 800l97 -100l-400 -400l-100 100l-200 -203l-100 100l300 303l100 -100zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="71" unicode=""
|
||||
d="M0 700h100v-100h-100v100zM200 700h100v-100h-100v100zM400 700h100v-100h-100v100zM600 700h100v-100h-100v100zM0 500h100v-100h-100v100zM200 500h100v-100h-100v100zM400 500h100v-100h-100v100zM600 500h100v-100h-100v100zM0 300h100v-100h-100v100zM200 300h100
|
||||
v-100h-100v100zM400 300h100v-100h-100v100zM600 300h100v-100h-100v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100zM600 100h100v-100h-100v100z" />
|
||||
<glyph glyph-name="72" unicode=""
|
||||
d="M0 800h200v-200h-200v200zM300 800h200v-200h-200v200zM600 800h200v-200h-200v200zM0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200zM0 200h200v-200h-200v200zM300 200h200v-200h-200v200zM600 200h200v-200h-200v200z" />
|
||||
<glyph glyph-name="73" unicode=""
|
||||
d="M0 800h300v-300h-300v300zM500 800h300v-300h-300v300zM0 300h300v-300h-300v300zM500 300h300v-300h-300v300z" />
|
||||
<glyph glyph-name="74" unicode=""
|
||||
d="M19 800h662c11 0 19 -8 19 -19v-331c0 -28 -22 -50 -50 -50h-600c-28 0 -50 22 -50 50v331c0 11 8 19 19 19zM0 309c16 -6 32 -9 50 -9h600c18 0 34 3 50 9v-290c0 -11 -8 -19 -19 -19h-662c-11 0 -19 8 -19 19v290zM550 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
|
||||
s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="75" unicode=""
|
||||
d="M0 700h300v-100h-50c-28 0 -50 -22 -50 -50v-150h300v150c0 28 -22 50 -50 50h-50v100h300v-100h-50c-28 0 -50 -22 -50 -50v-400c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50v150h-300v-150c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50
|
||||
v400c0 28 -22 50 -50 50h-50v100z" />
|
||||
<glyph glyph-name="76" unicode=""
|
||||
d="M400 700c165 0 300 -135 300 -300v-100h50c28 0 50 -22 50 -50v-200c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v350c0 111 -89 200 -200 200s-200 -89 -200 -200v-350c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v200c0 28 22 50 50 50h50v100
|
||||
c0 165 135 300 300 300z" />
|
||||
<glyph glyph-name="77" unicode=""
|
||||
d="M0 500c0 109 91 200 200 200s200 -91 200 -200c0 109 91 200 200 200s200 -91 200 -200c0 -55 -23 -105 -59 -141l-341 -340l-341 340c-36 36 -59 86 -59 141z" />
|
||||
<glyph glyph-name="78" unicode=""
|
||||
d="M400 700l400 -300l-100 3v-403h-200v200h-200v-200h-200v400h-100z" />
|
||||
<glyph glyph-name="79" unicode=""
|
||||
d="M0 800h800v-800h-800v800zM100 700v-300l100 100l400 -400h100v100l-200 200l100 100l100 -100v300h-600z" />
|
||||
<glyph glyph-name="7a" unicode=""
|
||||
d="M19 800h762c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-762c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 600v-300h100l100 -100h200l100 100h100v300h-600z" />
|
||||
<glyph glyph-name="7b" unicode=""
|
||||
d="M200 600c80 0 142 -56 200 -122c58 66 119 122 200 122c131 0 200 -101 200 -200s-69 -200 -200 -200c-81 0 -142 56 -200 122c-58 -66 -121 -122 -200 -122c-131 0 -200 101 -200 200s69 200 200 200zM200 500c-74 0 -100 -54 -100 -100s26 -100 100 -100
|
||||
c42 0 88 47 134 100c-46 53 -92 100 -134 100zM600 500c-43 0 -88 -47 -134 -100c46 -53 91 -100 134 -100c74 0 100 54 100 100s-26 100 -100 100z" />
|
||||
<glyph glyph-name="7c" unicode="" horiz-adv-x="400"
|
||||
d="M300 800c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100zM150 550c83 0 150 -69 150 -150c0 -66 -100 -214 -100 -250c0 -28 22 -50 50 -50s50 22 50 50h100c0 -83 -67 -150 -150 -150s-150 64 -150 150s100 222 100 250s-22 50 -50 50
|
||||
s-50 -22 -50 -50h-100c0 83 67 150 150 150z" />
|
||||
<glyph glyph-name="7d" unicode=""
|
||||
d="M200 800h500v-100h-122c-77 -197 -156 -392 -234 -588l-6 -12h162v-100h-500v100h122c77 197 156 392 234 588l7 12h-163v100z" />
|
||||
<glyph glyph-name="7e" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="7f" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="80" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="81" unicode=""
|
||||
d="M550 800c138 0 250 -112 250 -250s-112 -250 -250 -250c-16 0 -32 0 -47 3l-3 -3v-100h-200v-200h-300v200l303 303c-3 15 -3 31 -3 47c0 138 112 250 250 250zM600 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
|
||||
<glyph glyph-name="82" unicode=""
|
||||
d="M134 600h3h4h4h5h500c28 0 50 -22 50 -50v-350h100v-150c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v150h100v350v2c0 20 15 42 34 48zM200 500v-300h100v-100h200v100h100v300h-400z" />
|
||||
<glyph glyph-name="83" unicode=""
|
||||
d="M0 800h400v-400h-400v400zM500 600h100v-400h-400v100h300v300zM700 400h100v-400h-400v100h300v300z" />
|
||||
<glyph glyph-name="84" unicode="" horiz-adv-x="600"
|
||||
d="M337 694c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-300 -150c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49zM437 544c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-400 -200c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50
|
||||
c0 21 16 44 37 49zM437 344c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-106 -56c24 -4 43 -26 43 -50c0 -28 -23 -51 -51 -51c-2 0 -6 1 -8 1h-200c-26 1 -48 24 -48 50c0 16 12 36 26 44zM151 -50c0 23 20 50 46 50h3h4h5h100c28 0 50 -22 50 -50
|
||||
s-22 -50 -50 -50h-100c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
|
||||
<glyph glyph-name="85" unicode=""
|
||||
d="M199 800h100v-200h-200v100h100v100zM586 797h1c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0l-44 -44
|
||||
c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43l43 44c32 33 72 53 128 56zM208 490c4 5 14 16 22 16h3c2 0 6 1 8 1c28 0 50 -22 50 -50c0 -11 -6 -27 -14 -35l-150 -150c-40 -40 -39 -105 0 -144c41 -41 110 -34 144 0l44 44c8 13 27 24 42 24
|
||||
c28 0 50 -22 50 -50c0 -15 -11 -35 -24 -43l-43 -44c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281zM499 200h200v-100h-100v-100h-100v200z" />
|
||||
<glyph glyph-name="86" unicode=""
|
||||
d="M586 797c18 1 39 1 57 -3c36 -8 69 -26 97 -54c78 -78 78 -203 0 -281l-150 -150c-62 -62 -132 -81 -182 -78s-69 17 -84 25s-26 27 -26 44c0 28 22 51 50 51c8 0 19 -3 26 -7c0 0 15 -11 41 -13s62 3 106 47l150 150c40 40 39 105 0 144c-41 41 -110 34 -144 0
|
||||
c-8 -13 -28 -24 -43 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43c32 33 72 53 128 56zM386 566c50 -2 64 -17 85 -22s37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11c0 0 -19 9 -47 10s-63 -4 -103 -44l-150 -150c-40 -40 -39 -105 0 -144c41 -41 110 -34 144 0
|
||||
c8 13 27 24 42 24c28 0 50 -22 50 -50c0 -15 -10 -35 -23 -43c-22 -22 -48 -37 -75 -47c-70 -25 -151 -9 -207 47c-78 78 -78 203 0 281l150 150c60 60 128 78 178 76z" />
|
||||
<glyph glyph-name="87" unicode=""
|
||||
d="M0 700h300v-300h-300v300zM400 700h400v-100h-400v100zM400 500h300v-100h-300v100zM0 300h300v-300h-300v300zM400 300h400v-100h-400v100zM400 100h300v-100h-300v100z" />
|
||||
<glyph glyph-name="88" unicode=""
|
||||
d="M50 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 700h600v-100h-600v100zM50 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 500h600v-100h-600v100zM50 300c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
|
||||
s22 50 50 50zM200 300h600v-100h-600v100zM50 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="89" unicode=""
|
||||
d="M800 800l-400 -800l-100 300l-300 100z" />
|
||||
<glyph glyph-name="8a" unicode="" horiz-adv-x="600"
|
||||
d="M300 700c110 0 200 -90 200 -200v-100h100v-400h-600v400h100v100c0 110 90 200 200 200zM300 600c-56 0 -100 -44 -100 -100v-100h200v100c0 56 -44 100 -100 100z" />
|
||||
<glyph glyph-name="8b" unicode="" horiz-adv-x="600"
|
||||
d="M300 800c110 0 200 -90 200 -200v-200h100v-400h-600v400h400v200c0 56 -44 100 -100 100s-100 -44 -100 -100h-100c0 110 90 200 200 200z" />
|
||||
<glyph glyph-name="8c" unicode=""
|
||||
d="M400 700v-100c-111 0 -200 -89 -200 -200h100l-150 -200l-150 200h100c0 165 135 300 300 300zM650 600l150 -200h-100c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-100z" />
|
||||
<glyph glyph-name="8d" unicode=""
|
||||
d="M100 800h600v-300h100l-150 -250l-150 250h100v200h-400v-100h-100v200zM150 550l150 -250h-100v-200h400v100h100v-200h-600v300h-100z" />
|
||||
<glyph glyph-name="8e" unicode=""
|
||||
d="M600 700l200 -150l-200 -150v100h-500v-100h-100v100c0 55 45 100 100 100h500v100zM200 300v-100h500v100h100v-100c0 -55 -45 -100 -100 -100h-500v-100l-200 150z" />
|
||||
<glyph glyph-name="8f" unicode="" horiz-adv-x="900"
|
||||
d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c5 -3 12 -8 16 -12l100 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 100c-4 3 -9 9 -12 13c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 200
|
||||
c142 0 250 108 250 250c0 139 -111 250 -250 250s-250 -111 -250 -250s111 -250 250 -250z" />
|
||||
<glyph glyph-name="90" unicode="" horiz-adv-x="600"
|
||||
d="M300 800c166 0 300 -134 300 -300c0 -200 -300 -500 -300 -500s-300 300 -300 500c0 166 134 300 300 300zM300 700c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200z" />
|
||||
<glyph glyph-name="91" unicode="" horiz-adv-x="900"
|
||||
d="M0 800h800v-541c1 -3 1 -8 1 -11s0 -7 -1 -10v-238h-800v800zM495 250c0 26 22 50 50 50h5h150v400h-600v-600h600v100h-150h-5c-28 0 -50 22 -50 50zM350 600c83 0 150 -67 150 -150c0 -100 -150 -250 -150 -250s-150 150 -150 250c0 83 67 150 150 150zM350 500
|
||||
c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="92" unicode="" horiz-adv-x="600"
|
||||
d="M0 700h200v-600h-200v600zM400 700h200v-600h-200v600z" />
|
||||
<glyph glyph-name="93" unicode="" horiz-adv-x="600"
|
||||
d="M0 700l600 -300l-600 -300v600z" />
|
||||
<glyph glyph-name="94" unicode="" horiz-adv-x="600"
|
||||
d="M300 700c166 0 300 -134 300 -300s-134 -300 -300 -300s-300 134 -300 300s134 300 300 300z" />
|
||||
<glyph glyph-name="95" unicode=""
|
||||
d="M400 700v-600l-400 300zM400 400l400 300v-600z" />
|
||||
<glyph glyph-name="96" unicode=""
|
||||
d="M0 700l400 -300l-400 -300v600zM400 100v600l400 -300z" />
|
||||
<glyph glyph-name="97" unicode=""
|
||||
d="M0 700h200v-600h-200v600zM200 400l500 300v-600z" />
|
||||
<glyph glyph-name="98" unicode=""
|
||||
d="M0 700l500 -300l-500 -300v600zM500 100v600h200v-600h-200z" />
|
||||
<glyph glyph-name="99" unicode="" horiz-adv-x="600"
|
||||
d="M0 700h600v-600h-600v600z" />
|
||||
<glyph glyph-name="9a" unicode=""
|
||||
d="M200 800h400v-200h200v-400h-200v-200h-400v200h-200v400h200v200z" />
|
||||
<glyph glyph-name="9b" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 403h800v-100h-800v100zM0 103h800v-100h-800v100z" />
|
||||
<glyph glyph-name="9c" unicode="" horiz-adv-x="600"
|
||||
d="M278 700c7 2 13 4 22 4c55 0 100 -45 100 -100v-4v-200c0 -55 -45 -100 -100 -100s-100 45 -100 100v200v2c0 44 35 88 78 98zM34 500h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-50c0 -111 89 -200 200 -200s200 89 200 200v50c0 28 22 50 50 50s50 -22 50 -50v-50
|
||||
c0 -148 -109 -270 -250 -294v-106h50c55 0 100 -45 100 -100h-400c0 55 45 100 100 100h50v106c-141 24 -250 146 -250 294v50v2c0 20 15 42 34 48z" />
|
||||
<glyph glyph-name="9d" unicode=""
|
||||
d="M0 500h800v-200h-800v200z" />
|
||||
<glyph glyph-name="9e" unicode=""
|
||||
d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-500c0 -28 -22 -50 -50 -50h-250v-100h100c55 0 100 -45 100 -100h-600c0 55 45 100 100 100h100v100h-250c-28 0 -50 22 -50 50v500v2c0 20 15 42 34 48zM100 600v-400h600v400h-600z" />
|
||||
<glyph glyph-name="9f" unicode=""
|
||||
d="M272 700c-14 -40 -22 -83 -22 -128c0 -221 179 -400 400 -400c45 0 88 8 128 22c-53 -158 -202 -272 -378 -272c-221 0 -400 179 -400 400c0 176 114 325 272 378z" />
|
||||
<glyph glyph-name="a0" unicode=""
|
||||
d="M350 700l150 -150h-100v-150h150v100l150 -150l-150 -150v100h-150v-150h100l-150 -150l-150 150h100v150h-150v-100l-150 150l150 150v-100h150v150h-100z" />
|
||||
<glyph glyph-name="a1" unicode=""
|
||||
d="M800 800v-550c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v206c-201 -6 -327 -27 -400 -50v-397c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v409s100 100 600 100z" />
|
||||
<glyph glyph-name="a2" unicode="" horiz-adv-x="700"
|
||||
d="M499 700c51 0 102 -20 141 -59c78 -78 78 -203 0 -281l-250 -244c-48 -48 -127 -48 -175 0s-48 127 0 175l96 97l69 -69l-90 -94l-7 -3c-10 -10 -10 -28 0 -38s28 -10 38 0l250 247c37 40 39 102 0 141s-104 40 -144 0l-278 -275c-66 -69 -68 -179 0 -247
|
||||
c69 -69 181 -69 250 0l9 12l116 113l69 -69l-125 -125c-107 -107 -281 -107 -388 0s-107 281 0 388l278 272c39 39 90 59 141 59z" />
|
||||
<glyph glyph-name="a3" unicode=""
|
||||
d="M600 800l200 -200l-100 -100l-200 200zM400 600l200 -200l-400 -400h-200v200z" />
|
||||
<glyph glyph-name="a4" unicode=""
|
||||
d="M550 800c83 0 150 -90 150 -200s-67 -200 -150 -200c-22 0 -40 8 -59 19c6 26 9 52 9 81c0 84 -27 158 -72 212c27 52 71 88 122 88zM250 700c83 0 150 -90 150 -200s-67 -200 -150 -200s-150 90 -150 200s67 200 150 200zM725 384c44 -22 75 -66 75 -118v-166h-200v66
|
||||
c0 50 -17 96 -44 134c66 2 126 33 169 84zM75 284c45 -53 106 -84 175 -84s130 31 175 84c44 -22 75 -66 75 -118v-166h-500v166c0 52 31 96 75 118z" />
|
||||
<glyph glyph-name="a5" unicode=""
|
||||
d="M400 800c110 0 200 -112 200 -250s-90 -250 -200 -250s-200 112 -200 250s90 250 200 250zM191 300c54 -61 128 -100 209 -100s155 39 209 100c106 -5 191 -92 191 -200v-100h-800v100c0 108 85 195 191 200z" />
|
||||
<glyph glyph-name="a6" unicode="" horiz-adv-x="600"
|
||||
d="M19 800h462c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-462c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 700v-500h300v500h-300zM250 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="a7" unicode=""
|
||||
d="M350 800c17 0 34 -1 50 -3v-397l-297 297c63 64 150 103 247 103zM500 694c169 -25 300 -168 300 -344c0 -193 -157 -350 -350 -350c-85 0 -161 31 -222 81l272 272v341zM91 562l237 -234l-212 -212c-70 55 -116 138 -116 234c0 84 35 158 91 212z" />
|
||||
<glyph glyph-name="a8" unicode=""
|
||||
d="M92 650c0 23 20 50 46 50h3h4h5h400c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-200h100c55 0 100 -45 100 -100h-300v-300l-56 -100l-44 100v300h-300c0 55 45 100 100 100h100v200h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
|
||||
<glyph glyph-name="a9" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 600v-400l300 200z" />
|
||||
<glyph glyph-name="aa" unicode=""
|
||||
d="M300 800h200v-300h300v-200h-300v-300h-200v300h-300v200h300v300z" />
|
||||
<glyph glyph-name="ab" unicode=""
|
||||
d="M300 800h100v-400h-100v400zM172 656l62 -78l-40 -31c-58 -46 -94 -117 -94 -197c0 -139 111 -250 250 -250s250 111 250 250c0 80 -39 151 -97 197l-37 31l62 78l38 -31c82 -64 134 -164 134 -275c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 111 53 211 134 275z
|
||||
" />
|
||||
<glyph glyph-name="ac" unicode=""
|
||||
d="M200 800h400v-200h-400v200zM9 500h782c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-91v200h-600v-200h-91c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM200 300h400v-300h-400v300z" />
|
||||
<glyph glyph-name="ad" unicode=""
|
||||
d="M0 700h100v-700h-100v700zM700 700h100v-700h-100v700zM200 600h200v-100h-200v100zM300 400h200v-100h-200v100zM400 200h200v-100h-200v100z" />
|
||||
<glyph glyph-name="ae" unicode=""
|
||||
d="M325 700c42 -141 87 -280 131 -419c29 74 59 148 88 222c30 -57 58 -114 87 -172h169v-100h-231l-13 28c-37 -92 -74 -184 -112 -275c-38 129 -79 257 -119 385c-42 -133 -83 -267 -125 -400c-28 88 -56 175 -84 262h-116v100h188l9 -34l3 -6c42 137 83 273 125 409z" />
|
||||
<glyph glyph-name="af" unicode=""
|
||||
d="M200 600c0 57 43 100 100 100s100 -43 100 -100c0 -28 -18 -48 -28 -72c-3 -6 -3 -16 -3 -28h231v-231c12 0 22 0 28 3c24 10 44 28 72 28c57 0 100 -43 100 -100s-43 -100 -100 -100c-28 0 -48 18 -72 28c-6 3 -16 3 -28 3v-231h-231c0 12 0 22 3 28c10 24 28 44 28 72
|
||||
c0 57 -43 100 -100 100s-100 -43 -100 -100c0 -28 18 -48 28 -72c3 -6 3 -16 3 -28h-231v600h231c0 12 0 22 -3 28c-10 24 -28 44 -28 72z" />
|
||||
<glyph glyph-name="b0" unicode="" horiz-adv-x="500"
|
||||
d="M247 700c84 0 148 -20 191 -59s59 -93 59 -141c0 -117 -69 -181 -119 -225s-81 -67 -81 -150v-25h-100v25c0 117 65 181 115 225s85 67 85 150c0 25 -8 48 -28 66s-56 34 -122 34s-97 -18 -116 -37s-27 -43 -31 -69l-100 12c5 38 19 88 59 128s103 66 188 66zM197 0h100
|
||||
v-100h-100v100z" />
|
||||
<glyph glyph-name="b1" unicode=""
|
||||
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -69 -48 -127 -112 -144c-22 55 -75 94 -138 94c-20 0 -39 -5 -56 -12c-17 64 -75 112 -144 112s-127 -48 -144 -112c-17 7 -36 12 -56 12c-37 0 -71 -12 -97 -34c-33 36 -53 82 -53 134
|
||||
c0 110 90 200 200 200c23 114 129 200 250 200zM334 300h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-200c0 -28 -22 -50 -50 -50s-50 22 -50 50v200v2c0 20 15 42 34 48zM134 200h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2
|
||||
c0 20 15 42 34 48zM534 200h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2c0 20 15 42 34 48z" />
|
||||
<glyph glyph-name="b2" unicode=""
|
||||
d="M600 800l200 -150l-200 -150v100h-50l-153 -191l175 -206l6 -3h22v100l200 -150l-200 -150v100h-25c-35 0 -56 12 -78 38l-166 190l-153 -190c-22 -27 -43 -38 -78 -38h-100v100h100l166 206l-163 191l-3 3h-100v100h100c34 0 56 -12 78 -38l153 -178l141 178
|
||||
c22 27 43 38 78 38h50v100z" />
|
||||
<glyph glyph-name="b3" unicode=""
|
||||
d="M400 800c110 0 209 -47 281 -119l119 119v-300h-300l109 109c-54 55 -126 91 -209 91c-166 0 -300 -134 -300 -300s134 -300 300 -300c83 0 158 34 212 88l72 -72c-72 -72 -174 -116 -284 -116c-220 0 -400 180 -400 400s180 400 400 400z" />
|
||||
<glyph glyph-name="b4" unicode=""
|
||||
d="M400 800h400v-400l-166 166l-400 -400l166 -166h-400v400l166 -166l400 400z" />
|
||||
<glyph glyph-name="b5" unicode="" horiz-adv-x="600"
|
||||
d="M250 800l250 -300h-200v-200h200l-250 -300l-250 300h200v200h-200z" />
|
||||
<glyph glyph-name="b6" unicode=""
|
||||
d="M300 600v-200h200v200l300 -250l-300 -250v200h-200v-200l-300 250z" />
|
||||
<glyph glyph-name="b7" unicode=""
|
||||
d="M0 800c441 0 800 -359 800 -800h-200c0 333 -267 600 -600 600v200zM0 500c275 0 500 -225 500 -500h-200c0 167 -133 300 -300 300v200zM0 200c110 0 200 -90 200 -200h-200v200z" />
|
||||
<glyph glyph-name="b8" unicode=""
|
||||
d="M100 800c386 0 700 -314 700 -700h-100c0 332 -268 600 -600 600v100zM100 600c276 0 500 -224 500 -500h-100c0 222 -178 400 -400 400v100zM100 400c165 0 300 -135 300 -300h-100c0 111 -89 200 -200 200v100zM100 200c55 0 100 -45 100 -100s-45 -100 -100 -100
|
||||
s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="b9" unicode=""
|
||||
d="M300 800h400c55 0 100 -45 100 -100v-200h-400v150c0 28 -22 50 -50 50s-50 -22 -50 -50v-250h400v-300c0 -55 -45 -100 -100 -100h-500c-55 0 -100 45 -100 100v200h100v-150c0 -28 22 -50 50 -50s50 22 50 50v550c0 55 45 100 100 100z" />
|
||||
<glyph glyph-name="ba" unicode=""
|
||||
d="M75 700h225v-100h-200v-500h400v100h100v-125c0 -41 -34 -75 -75 -75h-450c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM600 700l200 -200l-200 -200v100h-200c-94 0 -173 -65 -194 -153c23 199 189 353 394 353v100z" />
|
||||
<glyph glyph-name="bb" unicode=""
|
||||
d="M500 700l300 -284l-300 -316v200h-100c-200 0 -348 -102 -400 -300c0 295 100 500 500 500v200z" />
|
||||
<glyph glyph-name="bc" unicode=""
|
||||
d="M381 791l19 9l19 -9c127 -53 253 -108 381 -160v-31c0 -166 -67 -313 -147 -419c-40 -53 -83 -97 -125 -128s-82 -53 -128 -53s-86 22 -128 53s-85 75 -125 128c-80 107 -147 253 -147 419v31c128 52 254 107 381 160zM400 100v591l-294 -122c8 -126 58 -243 122 -328
|
||||
c35 -46 73 -86 106 -110s62 -31 66 -31z" />
|
||||
<glyph glyph-name="bd" unicode=""
|
||||
d="M600 800h100v-800h-100v800zM400 700h100v-700h-100v700zM200 500h100v-500h-100v500zM0 300h100v-300h-100v300z" />
|
||||
<glyph glyph-name="be" unicode=""
|
||||
d="M300 800h100v-200h200l100 -100l-100 -100h-200v-400h-100v500h-200l-100 100l100 100h200v100z" />
|
||||
<glyph glyph-name="bf" unicode=""
|
||||
d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h200v-100h-200v100zM400 600h300v-100h-300v100zM400 400h400v-100h-400v100z" />
|
||||
<glyph glyph-name="c0" unicode=""
|
||||
d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h400v-100h-400v100zM400 600h300v-100h-300v100zM400 400h200v-100h-200v100z" />
|
||||
<glyph glyph-name="c1" unicode=""
|
||||
d="M75 700h650c41 0 75 -34 75 -75v-550c0 -41 -34 -75 -75 -75h-650c-41 0 -75 34 -75 75v550c0 41 34 75 75 75zM100 600v-100h100v100h-100zM300 600v-100h400v100h-400zM100 400v-100h100v100h-100zM300 400v-100h400v100h-400zM100 200v-100h100v100h-100zM300 200
|
||||
v-100h400v100h-400z" />
|
||||
<glyph glyph-name="c2" unicode=""
|
||||
d="M400 800l100 -300h300l-250 -200l100 -300l-250 200l-250 -200l100 300l-250 200h300z" />
|
||||
<glyph glyph-name="c3" unicode=""
|
||||
d="M400 800c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM650 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 600c110 0 200 -90 200 -200
|
||||
s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM50 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM750 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
|
||||
s22 50 50 50zM650 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="c4" unicode=""
|
||||
d="M34 800h632c18 0 34 -16 34 -34v-732c0 -18 -16 -34 -34 -34h-632c-18 0 -34 16 -34 34v732c0 18 16 34 34 34zM100 700v-500h500v500h-500zM350 150c-38 0 -63 -42 -44 -75s69 -33 88 0s-6 75 -44 75z" />
|
||||
<glyph glyph-name="c5" unicode=""
|
||||
d="M0 800h300l500 -500l-300 -300l-500 500v300zM200 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
|
||||
<glyph glyph-name="c6" unicode=""
|
||||
d="M0 600h200l300 -300l-200 -200l-300 300v200zM340 600h160l300 -300l-200 -200l-78 78l119 122zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="c7" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200
|
||||
s90 200 200 200zM400 500c-56 0 -100 -44 -100 -100s44 -100 100 -100s100 44 100 100s-44 100 -100 100z" />
|
||||
<glyph glyph-name="c8" unicode=""
|
||||
d="M0 700h559l-100 -100h-359v-500h500v159l100 100v-359h-700v700zM700 700l100 -100l-400 -400l-200 200l100 100l100 -100z" />
|
||||
<glyph glyph-name="c9" unicode=""
|
||||
d="M9 800h782c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM150 722l-72 -72l100 -100l-100 -100l72 -72l172 172zM400 500v-100h300v100h-300z" />
|
||||
<glyph glyph-name="ca" unicode=""
|
||||
d="M0 800h800v-200h-50c0 55 -45 100 -100 100h-150v-550c0 -28 22 -50 50 -50h50v-100h-400v100h50c28 0 50 22 50 50v550h-150c-55 0 -100 -45 -100 -100h-50v200z" />
|
||||
<glyph glyph-name="cb" unicode=""
|
||||
d="M0 700h100v-400h-100v400zM200 700h350c21 0 39 -13 47 -31c0 0 103 -291 103 -319s-22 -50 -50 -50h-150c-28 0 -50 -25 -50 -50s39 -158 47 -184s-5 -55 -31 -63s-52 5 -66 31s-109 219 -128 238s-44 28 -72 28v400z" />
|
||||
<glyph glyph-name="cc" unicode=""
|
||||
d="M400 666c10 19 28 32 47 34l19 -3c26 -8 39 -37 31 -63s-47 -159 -47 -184s22 -50 50 -50h150c28 0 50 -22 50 -50s-103 -319 -103 -319c-8 -18 -26 -31 -47 -31h-350v400c28 0 53 9 72 28s114 212 128 238zM0 400h100v-400h-100v400z" />
|
||||
<glyph glyph-name="cd" unicode=""
|
||||
d="M200 700h300v-100h-100v-6c25 -4 50 -8 72 -16l-34 -94c-28 11 -58 16 -88 16c-139 0 -250 -111 -250 -250s111 -250 250 -250s250 111 250 250c0 31 -5 60 -16 88l91 37c14 -38 25 -81 25 -125c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 176 130 323 300 347v3
|
||||
h-100v100zM700 584c0 0 -296 -348 -316 -368s-48 -20 -68 0s-20 48 0 68s384 300 384 300z" />
|
||||
<glyph glyph-name="ce" unicode=""
|
||||
d="M600 700l200 -150l-200 -150v100h-600v100h600v100zM200 300v-100h600v-100h-600v-100l-200 150z" />
|
||||
<glyph glyph-name="cf" unicode=""
|
||||
d="M300 800h100c55 0 100 -45 100 -100h100c55 0 100 -45 100 -100h-700c0 55 45 100 100 100h100c0 55 45 100 100 100zM100 500h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-481c0 -11 -8 -19 -19 -19h-462
|
||||
c-11 0 -19 8 -19 19v481z" />
|
||||
<glyph glyph-name="d0" unicode=""
|
||||
d="M100 800h200v-400c0 -55 45 -100 100 -100s100 45 100 100v400h100v-400c0 -110 -90 -200 -200 -200h-50c-138 0 -250 90 -250 200v400zM0 100h700v-100h-700v100z" />
|
||||
<glyph glyph-name="d1" unicode=""
|
||||
d="M9 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM609 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282
|
||||
c0 6 3 9 9 9zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="d2" unicode=""
|
||||
d="M10 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM610 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 5 9 10 9zM310 600h181c6 0 9 -3 9 -9v-91h-200v91c0 6 4 9 10 9zM0 400h800v-100h-800v100zM0 200h200v-191c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v191zM300 200
|
||||
h200v-91c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v91zM600 200h200v-191c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v191z" />
|
||||
<glyph glyph-name="d3" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM9 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM609 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182
|
||||
c-6 0 -9 3 -9 9v482c0 6 3 9 9 9z" />
|
||||
<glyph glyph-name="d4" unicode=""
|
||||
d="M50 600h500c28 0 50 -22 50 -50v-150l100 100h100v-300h-100l-100 100v-150c0 -28 -22 -50 -50 -50h-500c-28 0 -50 22 -50 50v400c0 28 22 50 50 50z" />
|
||||
<glyph glyph-name="d5" unicode=""
|
||||
d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 600v100c26 0 52 -4 75 -10c130 -33 225 -150 225 -290s-95 -258 -225 -291h-3c-23 -6 -47 -9 -72 -9v100c17 0 34 2 50 6c86 22 150 100 150 194s-64 172 -150 194c-16 4 -33 6 -50 6zM500 500l25 -3
|
||||
c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z" />
|
||||
<glyph glyph-name="d6" unicode="" horiz-adv-x="600"
|
||||
d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 500l25 -3c44 -11 75 -51 75 -97s-32 -86 -75 -97l-25 -3v200z" />
|
||||
<glyph glyph-name="d7" unicode="" horiz-adv-x="400"
|
||||
d="M334 800h66v-800h-66l-134 200h-200v400h200z" />
|
||||
<glyph glyph-name="d8" unicode=""
|
||||
d="M309 800h82c6 0 10 -4 12 -9l294 -682l3 -19v-81c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v81l3 19l294 682c2 5 6 9 12 9zM300 500v-200h100v200h-100zM300 200v-100h100v100h-100z" />
|
||||
<glyph glyph-name="d9" unicode=""
|
||||
d="M375 800c138 0 269 -39 378 -109l-53 -82c-93 60 -205 91 -325 91c-119 0 -229 -32 -322 -91l-53 82c109 70 237 109 375 109zM375 500c78 0 154 -23 216 -62l-53 -85c-46 30 -104 47 -163 47c-60 0 -112 -17 -159 -47l-54 85c62 40 134 62 213 62zM375 200
|
||||
c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="da" unicode="" horiz-adv-x="900"
|
||||
d="M551 800c16 0 32 0 47 -3l-97 -97v-200h200l97 97c3 -15 3 -31 3 -47c0 -138 -112 -250 -250 -250c-32 0 -62 8 -90 19l-288 -291c-20 -20 -46 -28 -72 -28s-52 8 -72 28c-39 39 -39 105 0 144l291 287c-11 28 -19 59 -19 91c0 138 112 250 250 250zM101 150
|
||||
c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="db" unicode=""
|
||||
d="M141 700c84 -84 169 -167 253 -250c82 83 167 165 247 250l143 -141l-253 -253c84 -82 167 -166 253 -247l-143 -143c-81 86 -165 169 -247 253l-253 -253l-141 143c85 80 167 164 250 247c-83 84 -166 169 -250 253z" />
|
||||
<glyph glyph-name="dc" unicode=""
|
||||
d="M0 800h100l231 -300h38l231 300h100l-225 -300h225v-100h-300v-100h300v-100h-300v-200h-100v200h-300v100h300v100h-300v100h225z" />
|
||||
<glyph glyph-name="dd" unicode="" horiz-adv-x="900"
|
||||
d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
|
||||
c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM300 600h100v-100h100v-100h-100v-100h-100v100h-100v100h100v100z" />
|
||||
<glyph glyph-name="de" unicode="" horiz-adv-x="900"
|
||||
d="M350 800c193 0 350 -157 350 -350c0 -61 -17 -119 -44 -169c4 -2 10 -6 13 -9l103 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 103c-3 3 -7 9 -9 13c-50 -28 -108 -44 -169 -44c-193 0 -350 157 -350 350s157 350 350 350zM350 700
|
||||
c-139 0 -250 -111 -250 -250s111 -250 250 -250c62 0 119 23 163 60c7 11 19 25 31 31l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM200 500h300v-100h-300v100z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 54 KiB |
BIN
static/fonts/open-iconic.ttf
Executable file
BIN
static/fonts/open-iconic.ttf
Executable file
Binary file not shown.
BIN
static/fonts/open-iconic.woff
Executable file
BIN
static/fonts/open-iconic.woff
Executable file
Binary file not shown.
BIN
static/images/gopher_full.png
Normal file
BIN
static/images/gopher_full.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
BIN
static/images/gopher_head.png
Normal file
BIN
static/images/gopher_head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
static/images/spinner.gif
Normal file
BIN
static/images/spinner.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
203
static/index.html
Normal file
203
static/index.html
Normal file
@@ -0,0 +1,203 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Gitrob: Putting the Open Source in OSINT">
|
||||
<meta name="author" content="Michael Henriksen (@michenriksen)">
|
||||
|
||||
<title>Gitrob</title>
|
||||
|
||||
<link href="/stylesheets/bootstrap.css" rel="stylesheet">
|
||||
<link href="/stylesheets/openiconic.css" rel="stylesheet">
|
||||
<link href="/stylesheets/highlight.css" rel="stylesheet">
|
||||
<link href="/stylesheets/application.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-md navbar-light bg-light">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/images/gopher_head.png" width="30" height="20" class="d-inline-block" alt="">
|
||||
Gitrob</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main role="main" class="container">
|
||||
<br />
|
||||
<div id="stats_container">
|
||||
<div class="progress" style="height: 30px;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" id="progress_bar" style="width: 100%;" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">Initializing...</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="card text-center" id="card_findings">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" id="card_findings_value">0</h3>
|
||||
<p class="card-text" id="card_findings_desc">Findings</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card text-center" id="card_files">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" id="card_files_value">0</h3>
|
||||
<p class="card-text" id="card_files_desc">Files</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card text-center" id="card_commits">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" id="card_commits_value">0</h3>
|
||||
<p class="card-text" id="card_commits_desc">Commits</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card text-center" id="card_repositories">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" id="card_repositories_value">0</h3>
|
||||
<p class="card-text" id="card_repositories_desc">Repositories</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card text-center" id="card_targets">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" id="card_targets_value">0</h3>
|
||||
<p class="card-text" id="card_targets_desc">Targets</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card text-center" id="card_duration">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title" id="card_duration_value">00:00:00</h3>
|
||||
<p class="card-text" id="card_duration_desc">Duration</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<section id="page_findings">
|
||||
<h3>
|
||||
Findings
|
||||
<input class="form-control form-control-sm float-right" type="text" placeholder="Search..." id="findings_search">
|
||||
</h3>
|
||||
|
||||
<table class="table table-sm table-hover table-striped" id="table_findings">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-action">Action</th>
|
||||
<th scope="col" class="col-path">Path</th>
|
||||
<th scope="col" class="col-commit">Commit</th>
|
||||
<th scope="col" class="col-repository">Repository</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</main><!-- /.container -->
|
||||
|
||||
<footer>
|
||||
<div class="container text-center text-muted">
|
||||
<a href="https://github.com/michenriksen/gitrob" target="_blank">Gitrob</a> is a project by <a href="http://michenriksen.com" target="_blank">Michael Henriksen</a> · <a href="https://github.com/michenriksen/gitrob/issues/new" target="_blank">Report an issue</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="text/template" id="template_finding">
|
||||
<td class="col-action">
|
||||
<% if (Action == "Modify") { %>
|
||||
<span class="badge badge-primary">MODIFY</span>
|
||||
<% } else if (Action == "Insert") { %>
|
||||
<span class="badge badge-success">CREATE</span>
|
||||
<% } else if (Action == "Delete") { %>
|
||||
<span class="badge badge-danger">DELETE</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td class="col-path"><code>
|
||||
<a href="#"><%= this.formattedFilePath() %></a>
|
||||
</code></td>
|
||||
<td class="col-commit"><code><a href="<%- CommitUrl %>" rel="noopener noreferer" target="_blank"><%= this.model.shortCommitHash() %></a></code></th>
|
||||
<td class="col-repository"><a href="<%- RepositoryUrl %>" rel="noopener noreferer" target="_blank"><%- RepositoryOwner %>/<%- RepositoryName %></a></th>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="template_finding_modal">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><%- Description %></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<% if (this.isTestRelated()) { %>
|
||||
<div class="alert alert-warning" role="alert"><strong>Notice:</strong> This file looks to be testing related.</div>
|
||||
<% } %>
|
||||
<div class="btn-group btn-group-sm float-right">
|
||||
<button type="button" id="finding_view_raw" class="btn btn-secondary">Raw</button>
|
||||
<button type="button" id="finding_view_hexdump" class="btn btn-secondary">Hex dump</button>
|
||||
</div>
|
||||
<table class="finding-meta-table">
|
||||
<tr>
|
||||
<th>Path:</th>
|
||||
<td><code><strong><%- RepositoryOwner %></strong>/<strong><%- RepositoryName %></strong>/<%- FilePath %></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Author:</th>
|
||||
<td><%- CommitAuthor %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Message:</th>
|
||||
<td class="font-italic"><%= this.truncatedCommitMessage() %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ID:</th>
|
||||
<td>
|
||||
<code><%- Id %></code>
|
||||
<button id="finding_id_clipboard" class="btn btn-outline-secondary btn-sm" data-clipboard-text="<%- Id %>"><span class="oi oi-clipboard"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<div class="text-center" id="modal_file_spinner_container">
|
||||
<img class="spinner" src="/images/spinner.gif" alt="Loading file contents..." id="modal_file_spinner" />
|
||||
<p>Loading file contents...</p>
|
||||
</div>
|
||||
<div id="modal_file_contents_container">
|
||||
<pre id="modal_file_contents"></pre>
|
||||
<pre id="modal_file_hexdump"></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span class="text-muted font-italic font-weight-light"><span class="oi oi-lightbulb"></span> Tip: Browse findings by using the <span class="oi oi-arrow-left"></span> and <span class="oi oi-arrow-right"></span> arrow keys.</span>
|
||||
<a href="<%- FileUrl %>" rel="noopener noreferrer" target="_blank" class="btn btn-primary" role="button">View file on GitHub</a>
|
||||
<a href="<%- CommitUrl %>" rel="noopener noreferrer" target="_blank" class="btn btn-secondary" role="button">View commit on GitHub</a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<div class="modal" tabindex="-1" role="dialog" id="finding_modal">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/javascripts/jquery-3.3.1.js"></script>
|
||||
<script src="/javascripts/underscore.js"></script>
|
||||
<script src="/javascripts/backbone.js"></script>
|
||||
<script src="/javascripts/popper.js"></script>
|
||||
<script src="/javascripts/bootstrap.js"></script>
|
||||
<script src="/javascripts/clipboard.js"></script>
|
||||
<script src="/javascripts/hexdump.js"></script>
|
||||
<script src="/javascripts/application.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
406
static/javascripts/application.js
Normal file
406
static/javascripts/application.js
Normal file
@@ -0,0 +1,406 @@
|
||||
var Stats = Backbone.Model.extend({
|
||||
url: "/stats",
|
||||
defaults: {
|
||||
"Status": "initializing",
|
||||
"StartedAt": null,
|
||||
"FinishedAt": null,
|
||||
"Progress": 0,
|
||||
"Targets": 0,
|
||||
"Repositories": 0,
|
||||
"Commits": 0,
|
||||
"Files": 0,
|
||||
"Findings": 0,
|
||||
},
|
||||
isFinished: function() {
|
||||
return this.get("Status") === "finished";
|
||||
},
|
||||
duration: function() {
|
||||
if (this.get("StartedAt") === null) {
|
||||
return "00:00:00";
|
||||
}
|
||||
var end;
|
||||
var start = Date.parse(this.get("StartedAt"));
|
||||
if (this.isFinished()) {
|
||||
end = Date.parse(this.get("FinishedAt"));
|
||||
} else {
|
||||
end = Date.now();
|
||||
}
|
||||
var millis = end - start;
|
||||
var seconds = Math.floor(millis / 1000);
|
||||
var nullDate = new Date(null);
|
||||
nullDate.setSeconds(seconds);
|
||||
return nullDate.toISOString().substr(11, 8);
|
||||
},
|
||||
});
|
||||
window.stats = new Stats;
|
||||
|
||||
var Finding = Backbone.Model.extend({
|
||||
idAttribute: "Id",
|
||||
testFileIndicators: ["test", "_spec", "fixture", "mock", "stub", "fake", "demo", "sample"],
|
||||
shortCommitHash: function() {
|
||||
return this.get("CommitHash").substr(0, 7);
|
||||
},
|
||||
trimmedCommitMessage: function() {
|
||||
var message = this.get("CommitMessage").split("-----END PGP SIGNATURE-----", 2).pop();
|
||||
return message.replace(/^\s\s*/, "").replace(/\s\s*$/, "")
|
||||
},
|
||||
isTestRelated: function() {
|
||||
var path = this.get("FilePath").toLowerCase();
|
||||
for (var i = 0; i < this.testFileIndicators.length; i++) {
|
||||
if (path.indexOf(this.testFileIndicators[i]) > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
fileContentsUrl: function() {
|
||||
return ["/files", this.get("RepositoryOwner"), this.get("RepositoryName"), this.get("CommitHash"), this.get("FilePath")].join("/");
|
||||
},
|
||||
fileContents: function(callback, error) {
|
||||
$.ajax({
|
||||
url: this.fileContentsUrl(),
|
||||
success: callback,
|
||||
error: error
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var Findings = Backbone.Collection.extend({
|
||||
url: "/findings",
|
||||
model: Finding,
|
||||
});
|
||||
|
||||
window.findings = new Findings();
|
||||
|
||||
var StatsView = Backbone.View.extend({
|
||||
id: "stats_container",
|
||||
model: stats,
|
||||
pollingTicker: null,
|
||||
durationTicker: null,
|
||||
pollingInterval: 500,
|
||||
initialize: function() {
|
||||
this.listenTo(this.model, "change", this.render)
|
||||
this.startDurationTicker();
|
||||
this.startPolling();
|
||||
},
|
||||
render: function() {
|
||||
if (this.model.isFinished()) {
|
||||
this.stopPolling();
|
||||
this.stopDurationTicker();
|
||||
}
|
||||
if (this.model.hasChanged("Progress")) {
|
||||
this.updateProgress();
|
||||
}
|
||||
if (this.model.hasChanged("Findings")) {
|
||||
this.updateFindings();
|
||||
}
|
||||
if (this.model.hasChanged("Files")) {
|
||||
this.updateFiles();
|
||||
}
|
||||
if (this.model.hasChanged("Commits")) {
|
||||
this.updateCommits();
|
||||
}
|
||||
if (this.model.hasChanged("Repositories")) {
|
||||
this.updateRepositories();
|
||||
}
|
||||
if (this.model.hasChanged("Targets")) {
|
||||
this.updateTargets();
|
||||
}
|
||||
},
|
||||
startPolling: function() {
|
||||
this.pollingTicker = setInterval(function() {
|
||||
statsView.model.fetch();
|
||||
}, this.pollingInterval);
|
||||
},
|
||||
stopPolling: function() {
|
||||
if (this.pollingTicker !== null) {
|
||||
clearInterval(this.pollingTicker);
|
||||
}
|
||||
},
|
||||
startDurationTicker: function() {
|
||||
this.DurationTicker = setInterval(function() {
|
||||
statsView.updateDuration()
|
||||
}, 1000);
|
||||
},
|
||||
stopDurationTicker: function() {
|
||||
this.updateDuration();
|
||||
if (this.durationTicker !== null) {
|
||||
clearInterval(this.durationTicker);
|
||||
}
|
||||
},
|
||||
updateDuration: function() {
|
||||
$("#card_duration_value").text(this.model.duration());
|
||||
},
|
||||
updateProgress: function() {
|
||||
var status = this.statusToHuman();
|
||||
$("title").text("Gitrob: " + status);
|
||||
$("#progress_bar").text(status).css("width", this.model.get("Progress") + "%");
|
||||
if (this.model.isFinished()) {
|
||||
$("#progress_bar").removeClass("progress-bar-animated progress-bar-striped").css("width", "100%");
|
||||
}
|
||||
},
|
||||
updateFindings: function() {
|
||||
$("#card_findings_value").hide().text(this.model.get("Findings").toLocaleString()).fadeIn("fast");
|
||||
},
|
||||
updateFiles: function() {
|
||||
$("#card_files_value").hide().text(this.model.get("Files").toLocaleString()).fadeIn("fast");
|
||||
},
|
||||
updateCommits: function() {
|
||||
$("#card_commits_value").hide().text(this.model.get("Commits").toLocaleString()).fadeIn("fast");
|
||||
},
|
||||
updateRepositories: function() {
|
||||
$("#card_repositories_value").hide().text(this.model.get("Repositories").toLocaleString()).fadeIn("fast");
|
||||
},
|
||||
updateTargets: function() {
|
||||
$("#card_targets_value").hide().text(this.model.get("Targets").toLocaleString()).fadeIn("fast");
|
||||
},
|
||||
statusToHuman: function() {
|
||||
var status;
|
||||
switch(this.model.get("Status")) {
|
||||
case "initializing":
|
||||
status = "Initializing";
|
||||
break;
|
||||
case "gathering":
|
||||
status = "Gathering repositories";
|
||||
break;
|
||||
case "analyzing":
|
||||
status = "Analyzing repositories"
|
||||
break;
|
||||
case "finished":
|
||||
status = "Finished";
|
||||
break;
|
||||
default:
|
||||
status = "Unknown";
|
||||
break;
|
||||
}
|
||||
return status + " (" + parseInt(this.model.get("Progress")) + "%)";
|
||||
}
|
||||
});
|
||||
window.statsView = new StatsView({el: $("#stats_container")});
|
||||
|
||||
var FindingView = Backbone.View.extend({
|
||||
tagName: "tr",
|
||||
events: {
|
||||
"click td.col-path a": "showFinding",
|
||||
},
|
||||
template: _.template($("#template_finding").html()),
|
||||
render: function() {
|
||||
this.$el.html(this.template(this.model.attributes)).data("finding", this.model);
|
||||
if (this.model.isTestRelated()) {
|
||||
this.$el.addClass("test-related");
|
||||
}
|
||||
return this;
|
||||
},
|
||||
formattedFilePath: function() {
|
||||
var splits = this.model.get("FilePath").split("/");
|
||||
var filename = splits.pop();
|
||||
var directory = this.ellipsisize(splits.join("/"), 60, 25);
|
||||
if (directory === "") {
|
||||
return "<strong>" + _.escape(filename) + "</strong>";
|
||||
}
|
||||
return _.escape(directory) + "/" + "<strong>" + _.escape(filename) + "</strong>";
|
||||
},
|
||||
ellipsisize: function(str, minLength, edgeLength) {
|
||||
str = String(str);
|
||||
if (str.length < minLength || str.length <= (edgeLength * 2)) {
|
||||
return str;
|
||||
}
|
||||
var edge = Array(edgeLength + 1).join(".");
|
||||
var midLength = str.length - edgeLength * 2;
|
||||
var pattern = "(" + edge + ").{" + midLength + "}(" + edge + ")";
|
||||
return str.replace(new RegExp(pattern), "$1…$2");
|
||||
},
|
||||
showFinding: function(e) {
|
||||
e.preventDefault();
|
||||
this.markAsSelected();
|
||||
var modalView = new FindingModal({
|
||||
model: this.model,
|
||||
el: "#finding_modal .modal-content"
|
||||
});
|
||||
modalView.render();
|
||||
$("#finding_modal").modal();
|
||||
modalView.fetchFileContents();
|
||||
},
|
||||
markAsSelected: function() {
|
||||
this.$el.closest("tbody").find("tr.table-selected").removeClass("table-selected");
|
||||
this.$el.addClass("table-selected");
|
||||
},
|
||||
});
|
||||
|
||||
var FindingsView = Backbone.View.extend({
|
||||
collection: findings,
|
||||
initialize: function() {
|
||||
this.listenTo(this.collection, "add", this.renderFinding);
|
||||
this.listenTo(stats, "change:Findings", _.debounce(this.update, 500));
|
||||
$("#findings_search").on("keyup", _.debounce(this.searchFindings, 200));
|
||||
$("#finding_modal").on("show.bs.modal", function(event) {
|
||||
$(document).on("keydown", function(e) {
|
||||
switch(e.keyCode) {
|
||||
case 37:
|
||||
var finding = findingsView.previousFinding();
|
||||
break;
|
||||
case 39:
|
||||
var finding = findingsView.nextFinding();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (finding.length === 0) {
|
||||
return;
|
||||
}
|
||||
findingsView.activeFinding().removeClass("table-selected");
|
||||
finding.addClass("table-selected");
|
||||
var modalView = new FindingModal({
|
||||
model: finding.data("finding"),
|
||||
el: "#finding_modal .modal-content"
|
||||
});
|
||||
modalView.render();
|
||||
$("#finding_modal").modal();
|
||||
modalView.fetchFileContents();
|
||||
});
|
||||
})
|
||||
.on("hidden.bs.modal", function(event) {
|
||||
$(document).unbind("keydown");
|
||||
});
|
||||
},
|
||||
update: function() {
|
||||
this.collection.fetch();
|
||||
},
|
||||
renderFinding: function(finding) {
|
||||
var findingEl = new FindingView({model: finding}).render().el;
|
||||
$(findingEl).appendTo(this.$el);
|
||||
},
|
||||
activeFinding: function() {
|
||||
return this.$el.find("tr.table-selected");
|
||||
},
|
||||
nextFinding: function() {
|
||||
return this.activeFinding().nextAll("tr").not(".d-none").first();
|
||||
},
|
||||
previousFinding: function() {
|
||||
return this.activeFinding().prevAll("tr").not(".d-none").first();
|
||||
},
|
||||
searchFindings: function() {
|
||||
var needle = $.trim($("#findings_search").val()).toLowerCase();
|
||||
if (needle == "") {
|
||||
$("#table_findings tbody tr").removeClass("d-none");
|
||||
return;
|
||||
}
|
||||
$("#table_findings tbody tr").each(function() {
|
||||
var path = $(this).find("td.col-path").text().toLowerCase();
|
||||
var commit = $(this).find("td.col-commit").text().toLowerCase();
|
||||
var repository = $(this).find("td.col-repository").text().toLowerCase();
|
||||
if (path.indexOf(needle) > -1 || commit.indexOf(needle) > -1 || repository.indexOf(needle) > -1) {
|
||||
$(this).removeClass("d-none");
|
||||
} else {
|
||||
$(this).addClass("d-none");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
window.findingsView = new FindingsView({el: "#table_findings tbody"});
|
||||
|
||||
var FindingModal = Backbone.View.extend({
|
||||
template: _.template($("#template_finding_modal").html()),
|
||||
interestingStringPatterns: [
|
||||
/((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))/gmi,
|
||||
/([a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)/gmi,
|
||||
/((\w*:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6}))/gmi,
|
||||
/([a-f0-9\-\$\/]{20,})/gmi,
|
||||
/(username)/gmi,
|
||||
/(secret)/gmi,
|
||||
/(passw(or)?d)/gmi,
|
||||
/(cred(s|ential))/gmi,
|
||||
/(access(_|-|.)?token)/gmi,
|
||||
],
|
||||
events: {
|
||||
"click #finding_view_raw": "showRawContents",
|
||||
"click #finding_view_hexdump": "showHexDumpContents",
|
||||
},
|
||||
render: function() {
|
||||
this.$el.html(this.template(this.model.attributes));
|
||||
new ClipboardJS('.btn', {
|
||||
container: document.getElementById('finding_modal')
|
||||
});
|
||||
return this;
|
||||
},
|
||||
showRawContents: function() {
|
||||
$("#finding_view_raw").addClass("active");
|
||||
$("#finding_view_hexdump").removeClass("active");
|
||||
$("#modal_file_hexdump").hide();
|
||||
$("#modal_file_contents").show();
|
||||
},
|
||||
showHexDumpContents: function() {
|
||||
$("#finding_view_raw").removeClass("active");
|
||||
$("#finding_view_hexdump").addClass("active");
|
||||
$("#modal_file_contents").hide();
|
||||
$("#modal_file_hexdump").show();
|
||||
},
|
||||
truncatedCommitMessage: function() {
|
||||
var message = this.model.trimmedCommitMessage();
|
||||
if (message.length <= 150) {
|
||||
return _.escape(message);
|
||||
}
|
||||
return _.escape(message.substr(0, 150)) + "…";
|
||||
},
|
||||
isTestRelated: function() {
|
||||
return this.model.isTestRelated();
|
||||
},
|
||||
isBinary: function(data) {
|
||||
return /[\x00-\x08\x0E-\x1F]/.test(data);
|
||||
},
|
||||
highlightInterestingStrings: function(haystack) {
|
||||
this.interestingStringPatterns.forEach(function(pattern) {
|
||||
haystack = haystack.replace(pattern, "<mark>$1</mark>");
|
||||
});
|
||||
return haystack;
|
||||
},
|
||||
fetchFileContents: function() {
|
||||
if (this.model.get("Action") == "Delete") {
|
||||
$("#modal_file_spinner_container").fadeOut("fast", function() {
|
||||
$("#modal_file_contents_container").html("<div class='alert alert-info' role='alert'>View commit on GitHub to see contents of deleted files.</div>").fadeIn("fast");
|
||||
});
|
||||
return;
|
||||
}
|
||||
var context = this;
|
||||
this.model.fileContents(function(data) {
|
||||
var worker = new Worker("/javascripts/highlight_worker.js");
|
||||
worker.onmessage = function(event) {
|
||||
$("#modal_file_spinner_container").fadeOut("fast", _.bind(function() {
|
||||
var content = this.highlightInterestingStrings(event.data);
|
||||
$("#modal_file_contents").html(content);
|
||||
new Hexdump(data, {
|
||||
container: "modal_file_hexdump",
|
||||
base: "hex",
|
||||
width: 8,
|
||||
byteGrouping: 1,
|
||||
html: true,
|
||||
ascii: true,
|
||||
lineNumbers: true,
|
||||
style: {
|
||||
lineNumberLeft: '',
|
||||
lineNumberRight: ':',
|
||||
stringLeft: '|',
|
||||
stringRight: '|',
|
||||
hexLeft: '',
|
||||
hexRight: '',
|
||||
hexNull: '.',
|
||||
nonPrintable: '.',
|
||||
stringNull: '.',
|
||||
}
|
||||
});
|
||||
$("#modal_file_contents_container").fadeIn("fast");
|
||||
if (this.isBinary(data)) {
|
||||
this.showHexDumpContents();
|
||||
} else {
|
||||
this.showRawContents();
|
||||
}
|
||||
}, context));
|
||||
}
|
||||
worker.postMessage(data);
|
||||
}, function() {
|
||||
$("#modal_file_spinner_container").fadeOut("fast", function() {
|
||||
$("#modal_file_contents_container").html("<div class='alert alert-warning' role='alert'>File size too large to display inline. View file on GitHub.</div>").fadeIn("fast");
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
2
static/javascripts/backbone.js
Normal file
2
static/javascripts/backbone.js
Normal file
File diff suppressed because one or more lines are too long
7
static/javascripts/bootstrap.js
vendored
Normal file
7
static/javascripts/bootstrap.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
static/javascripts/clipboard.js
Normal file
7
static/javascripts/clipboard.js
Normal file
File diff suppressed because one or more lines are too long
16
static/javascripts/hexdump.js
Normal file
16
static/javascripts/hexdump.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// Hexdump.js 0.1.1
|
||||
// (c) 2011 Dustin Willis Webber
|
||||
// Hexdump is freely distributable under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://github.com/mephux/hexdump.js
|
||||
var Hexdump;
|
||||
Hexdump=function(){function f(c,b){var a=this;a.hexdump=[];a.hex=!1;a.options={container:b.container||"",width:b.width||16,byteGrouping:b.byteGrouping||0,ascii:b.ascii,lineNumber:b.lineNumber,endian:b.endian||"big",html:b.html,base:b.base||"hexadecimal",nonPrintable:b.nonPrintable||".",style:{lineNumberLeft:b.style.lineNumberLeft||"",lineNumberRight:b.style.lineNumberRight||":",stringLeft:b.style.stringLeft||"|",stringRight:b.style.stringRight||"|",hexLeft:b.style.hexLeft||"",hexRight:b.style.hexRight||"",
|
||||
hexNull:b.style.hexNull||".",stringNull:b.style.stringNull||" "}};if(a.options.base=="hex")a.hex=!0;else if(a.options.base=="hexadecimal")a.hex=!0;var d=a.options.lineNumber;if(typeof d=="undefined"||d==null)a.options.lineNumber=!0;d=a.options.ascii;if(typeof d=="undefined"||d==null)a.options.ascii=!1;d=a.options.html;if(typeof d=="undefined"||d==null)a.options.html=!0;if(a.endian!="little")a.endian="big";if(a.options.byteGrouping>c.length)a.options.byteGrouping=c.length;a.options.byteGrouping--;
|
||||
if(a.options.width>c.length)a.options.width=c.length;a.padding={hex:4,dec:5,bin:8};switch(a.options.base){case "hexadecimal":case "hex":case 16:a.setNullPadding(a.padding.hex);a.baseConvert=function(b){for(;0<b.length;)return a.addPadding(b[0].charCodeAt(0).toString(16),a.padding.hex)};break;case "decimal":case "dec":case 10:a.setNullPadding(a.padding.dec);a.baseConvert=function(b){for(;0<b.length;)return a.addPadding(b[0].charCodeAt(0),a.padding.dec)};break;case "binary":case "bin":case 2:a.setNullPadding(a.padding.bin);
|
||||
a.baseConvert=function(b){for(;0<b.length;){b=b[0].charCodeAt(0);for(var c="",d=0;d<8;d++)c=b%2+c,b=Math.floor(b/2);return a.addPadding(c,a.padding.bin)}};break;default:a.options.base="hex",a.hex=!0,a.setNullPadding(a.padding.hex),a.baseConvert=function(b){for(;0<b.length;)return a.addPadding(b[0].charCodeAt(0).toString(16),a.padding.hex)}}a.data=c.match(RegExp(".{1,"+this.options.width+"}","g"));a.nullCount=a.options.width-a.data[a.data.length-1].length;a.hexCounter=0;for(d=a.stringCounter=0;d<a.data.length;d++){var e=
|
||||
a.process(a.data[d]);a.hexdump.push({data:e.data,string:e.string,length:a.data[d].length,missing:a.options.width-a.data[d].length})}a.dump()}f.prototype.dump=function(){this.output="";for(var c=0;c<this.hexdump.length;c++){if(this.options.lineNumber){var b="";b+=this.options.style.lineNumberLeft;for(var a=c*this.options.width,d=8-a.toString().length,e=0;e<d;e++)b+="0";b+=a;b+=this.options.style.lineNumberRight+" ";this.output+=this.options.html?'<span id="line-number">'+b+"</span>":b}b=0;this.output+=
|
||||
this.options.style.hexLeft;for(a=0;a<this.hexdump[c].data.length;a++)b==this.options.byteGrouping?(this.output+=a==this.hexdump[c].data.length-1?this.hexdump[c].data[a]:this.hexdump[c].data[a]+" ",b=0):(this.output+=this.hexdump[c].data[a],b++);this.output+=this.options.style.hexRight;this.appendString(this.hexdump[c]);this.output+="\n"}document.getElementById(this.options.container).innerHTML=this.output};f.prototype.appendString=function(c){this.output+=" "+this.options.style.stringLeft;this.output+=
|
||||
c.string;this.output+=this.options.style.stringRight};f.prototype.splitNulls=function(c){var b=[],a="";if(c&&c.length>2)for(var d=0;d<c.length;d++)(d+1)%2==0?(a+=c[d].toString(),b.push(a),a=""):a+=c[d].toString();return b};f.prototype.process=function(c){for(var b=[],a=[],d=0;d<c.length;d++){if(this.options.html){var e=this.baseConvert(c[d]);if(this.hex){e=this.splitNulls(e);for(var f=0;f<e.length;f++)a.push('<span data-hex-id="'+this.hexCounter+'">'+e[f]+"</span>")}else a.push('<span data-hex-id="'+
|
||||
this.hexCounter+'">'+e+"</span>");b.push('<span data-string-id="'+this.hexCounter+'">'+this.checkForNonPrintable(c[d])+"</span>")}else{e=this.baseConvert(c[d]);if(this.hex){e=this.splitNulls(e);for(f=0;f<e.length;f++)a.push(e[f])}else a.push(e);b.push(this.checkForNonPrintable(c[d]))}this.hexCounter++}d=this.hex?this.options.width*2:this.options.width;if(a.length<d){c=d-a.length;for(d=0;d<c;d++)e="",e=this.options.html?'<span data-hex-null="true">'+this.options.style.hexNull+"</span>":this.options.style.hexNull,
|
||||
a.push(e)}if(b.length<this.options.width){c=this.options.width-b.length;for(d=0;d<c;d++)e="",e=this.options.html?'<span data-string-null="true">'+this.options.style.stringNull+"</span>":this.options.style.stringNull,b.push(e)}return{data:a,string:b.join("")}};f.prototype.setNullPadding=function(c){var b=this.options.style.hexNull[0];this.options.style.hexNull="";this.hex&&(c/=2);for(var a=0;a<c;a++)this.options.style.hexNull+=b};f.prototype.addPadding=function(c,b){for(var a=c.toString().length,d=
|
||||
"",e=0;e<b-a;e++)d+="0";return this.options.endian=="big"?d+c:c+d};f.prototype.checkForNonPrintable=function(c){var b=c.charCodeAt(0).toString(16);return b==9?".":b==127?".":b.length>2&&this.options.ascii?".":c};return f}();
|
||||
2
static/javascripts/highlight.js
Normal file
2
static/javascripts/highlight.js
Normal file
File diff suppressed because one or more lines are too long
5
static/javascripts/highlight_worker.js
Normal file
5
static/javascripts/highlight_worker.js
Normal file
@@ -0,0 +1,5 @@
|
||||
onmessage = function(event) {
|
||||
importScripts("/javascripts/highlight.js");
|
||||
var result = self.hljs.highlightAuto(event.data);
|
||||
postMessage(result.value);
|
||||
}
|
||||
2
static/javascripts/jquery-3.3.1.js
vendored
Normal file
2
static/javascripts/jquery-3.3.1.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
static/javascripts/popper.js
Normal file
5
static/javascripts/popper.js
Normal file
File diff suppressed because one or more lines are too long
6
static/javascripts/underscore.js
Normal file
6
static/javascripts/underscore.js
Normal file
File diff suppressed because one or more lines are too long
127
static/stylesheets/application.css
Normal file
127
static/stylesheets/application.css
Normal file
@@ -0,0 +1,127 @@
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
a.btn {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #303030;
|
||||
margin-top: 50px;
|
||||
padding: 20px 0px 100px 0px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.navbar a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#findings_search {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
#table_findings td.col-path {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
#table_findings td.col-path strong {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#table_findings .col-action {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#table_findings .col-action .badge {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#table_findings .col-commit {
|
||||
width: 70px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#table_findings .col-repository {
|
||||
width: 200px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#table_findings tr.test-related {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
display: block;
|
||||
margin: 25px auto 10px auto;
|
||||
}
|
||||
|
||||
tr.table-selected {
|
||||
background-color: #375a7f !important;
|
||||
}
|
||||
|
||||
#modal_file .alert-secondary {
|
||||
color: #ccc
|
||||
}
|
||||
|
||||
#modal_file .alert-secondary strong {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.finding-meta-table {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.finding-meta-table th {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#finding_id_clipboard {
|
||||
transform: scale(0.7);
|
||||
}
|
||||
|
||||
#modal_file_contents_container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#modal_file_contents, #modal_file_hexdump {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#modal_file_hexdump {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
#modal_file_hexdump #line-number {
|
||||
color: #00bc8c;
|
||||
}
|
||||
|
||||
#modal_file_hexdump span[data-string-id], #modal_file_hexdump span[data-string-null] {
|
||||
color: #F39C12;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
height: 15px;
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
12
static/stylesheets/bootstrap.css
vendored
Normal file
12
static/stylesheets/bootstrap.css
vendored
Normal file
File diff suppressed because one or more lines are too long
75
static/stylesheets/highlight.css
Normal file
75
static/stylesheets/highlight.css
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Tomorrow Night Theme */
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
|
||||
/* Tomorrow Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
/* Tomorrow Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-regexp,
|
||||
.hljs-deletion {
|
||||
color: #cc6666;
|
||||
}
|
||||
|
||||
/* Tomorrow Orange */
|
||||
.hljs-number,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params,
|
||||
.hljs-meta,
|
||||
.hljs-link {
|
||||
color: #de935f;
|
||||
}
|
||||
|
||||
/* Tomorrow Yellow */
|
||||
.hljs-attribute {
|
||||
color: #f0c674;
|
||||
}
|
||||
|
||||
/* Tomorrow Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition {
|
||||
color: #b5bd68;
|
||||
}
|
||||
|
||||
/* Tomorrow Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #81a2be;
|
||||
}
|
||||
|
||||
/* Tomorrow Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #b294bb;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #1d1f21;
|
||||
color: #c5c8c6;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
1
static/stylesheets/openiconic.css
Executable file
1
static/stylesheets/openiconic.css
Executable file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user