diff --git a/.appveyor.yml b/.appveyor.yml
index 5eb330ce5..b0eb39282 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -13,11 +13,11 @@ environment:
MSYS_BITS: 64
- TARGET: beta-i686-pc-windows-gnu
MSYS_BITS: 32
- - TARGET: 1.16.0-x86_64-pc-windows-msvc
- - TARGET: 1.16.0-i686-pc-windows-msvc
- - TARGET: 1.16.0-x86_64-pc-windows-gnu
+ - TARGET: 1.30.0-x86_64-pc-windows-msvc
+ - TARGET: 1.30.0-i686-pc-windows-msvc
+ - TARGET: 1.30.0-x86_64-pc-windows-gnu
MSYS_BITS: 64
- - TARGET: 1.16.0-i686-pc-windows-gnu
+ - TARGET: 1.30.0-i686-pc-windows-gnu
MSYS_BITS: 32
install:
@@ -30,8 +30,7 @@ install:
- cargo -vV
build_script:
- - cargo build
- - cargo package
+ - cargo build --features no-color
test_script:
- - cargo test
+ - cargo test --features no-color
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..f2551d690
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,29 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+**Bug description**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior (example):
+1. Download '...'
+2. Run '...'
+3. Check report at '...'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Setup (please complete the following information):**
+ - OS: [e.g. Windows 10]
+ - SUPER Version: [e.g. 0.5.0]
+ - Browser (for reports): [e.g. firefox, opera]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..066b2d920
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,17 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.gitignore b/.gitignore
index 9b083b3ee..797fd2cf0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,8 @@
downloads/*
dist/*
results/*
-target
+target/*
+releases/*
rpmbuild/*
!rpmbuild/super.spec
*.bk
diff --git a/.travis.yml b/.travis.yml
index a7973638d..439c16d09 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,100 +1,97 @@
language: rust
-# cache: cargo
+cache: cargo
dist: trusty
sudo: true
+services:
+- docker
+
os:
- - linux
- - osx
+- linux
+- osx
+
addons:
apt:
packages:
- - libcurl4-openssl-dev
- - libelf-dev
- - libdw-dev
- - cmake
- - gcc
- - binutils-dev
- - libiberty-dev
- - zlib1g-dev
+ - libcurl4-openssl-dev
+ - libelf-dev
+ - libdw-dev
+ - cmake
+ - gcc
+ - binutils-dev
+ - libiberty-dev
+ - zlib1g-dev
# Run builds for all the supported trains
rust:
- - nightly
- - beta
- - stable
- - 1.16.0
+- nightly
+- beta
+- stable
+- 1.30.0
+
+# Generate packages for multiple distributions.
+matrix:
+ include:
+ - os: linux
+ rust: "stable"
+ env: PACKAGE="debian"
+ - os: linux
+ rust: "stable"
+ env: PACKAGE="ubuntu"
+ - os: linux
+ rust: "stable"
+ env: PACKAGE="fedora"
+ - os: linux
+ rust: "stable"
+ env: PACKAGE="centos"
-# Load travis-cargo
before_script:
- - |
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then
- ( cargo install rustfmt --force || true );
- ( cargo install --git https://github.com/mmstick/cargo-deb.git || true );
- fi
- - |
- if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
- ( ( cargo install clippy && export CLIPPY=true ) || export CLIPPY=false );
- fi
- - export PATH=$PATH:~/.cargo/bin
+- export PATH=$PATH:~/.cargo/bin
+- "./travis-helper.sh install_deps"
+ # TODO: change tag when bumping version number. (Maintain in sync with Cargo.toml)
+- |
+ if [[ $TRAVIS_TAG ]]; then
+ export TAG=$TRAVIS_TAG;
+ else
+ export TAG="0.5.0";
+ fi
-# The main build
+# Run the multiple tests.
script:
- - cargo build
- - cargo package
- - cargo test
- - cargo build --features beta
- - cargo test --features beta
- - cargo build --features unstable
- - cargo test --features unstable
- - |
- if [[ "$TRAVIS_RUST_VERSION" == "nightly" && $CLIPPY ]]; then
- cargo clippy
- fi
- - |
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then
- cargo deb
- fi
-# Turning of format checking due to several rustfmt bugs.
-# - |
-# if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then
-# cargo fmt -- --write-mode=diff
-# fi
+- "./travis-helper.sh build"
+- "./travis-helper.sh package"
+- "./travis-helper.sh test"
+- "./travis-helper.sh test_ignored"
+- "./travis-helper.sh build_unstable"
+- "./travis-helper.sh test_unstable"
+- "./travis-helper.sh test_unstable_ignored"
+- "./travis-helper.sh clippy_run"
+- "./travis-helper.sh dist_test"
+- "./travis-helper.sh fmt_run"
-# Send coverage reports and upload docs
+# Upload code coverage report and documentation.
after_success:
- # Coverage report
- - |
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then
- wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
- tar xzf master.tar.gz &&
- cd kcov-master &&
- mkdir build &&
- cd build &&
- cmake .. &&
- make &&
- sudo make install &&
- cd ../.. &&
- rm -rf kcov-master &&
- for file in target/debug/super-*; do mkdir -p "target/cov/$(basename $file)"; kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done &&
- bash <(curl -s https://codecov.io/bash) &&
- echo "Uploaded code coverage"
- fi
- # Documentation upload
- - |
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" ]]; then
- cargo doc &&
- echo "" > target/doc/index.html &&
- git clone https://github.com/davisp/ghp-import.git &&
- ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc &&
- echo "Uploaded documentation"
- fi
+- "./travis-helper.sh upload_code_coverage"
+- "./travis-helper.sh upload_documentation"
+
+deploy:
+ provider: releases
+ api_key:
+ secure: n19EgLXHFh8pXBulAfOFtzIMFPSm0yM0yi/sXY1bQgr+QztAHwxkGKaCU/y4yqYwDNH0hCFQn+P8X//Yucz/Cvw2Ng2G040hMJqsu/RztX5svq+pjmTNYX9QuwOg/6AnFoQs2zxRMZia4npVDA66q78G5GA+2eKZ/cDc5/6M32uh7RH8d6yYDnyCJ1lOk35tFu7DLZtfC5xQvOeC+iD4QIiKtVitcjRLQPiV71cG42a0wYYjX3sVKCjqF7NX/q5VHLxG0EofaLeyAI2NdK9RkuAfd6y8YyR0XNwlvq0qX8j5iDtc6ljzOtytlPYUlvyF3AqOJdVt+rBiI2/tZDtVkOobO+hS/+Tl680PpiX+m/HaHErgV0pMYCavYQkjPyMVE0DJCMTMCoHFhGMB+gC/piagVBZ9lKFHRgxXYBFhJVoco2vqMZDm85avI7IzL9YNtJqRt0h2JJZ4/42/Hn0+52CtXJI86HwUU4hD0eUsm9yyWNOsrDahmra1H28vxS7fjEb1Sozol3fMRWKMllKqsUr8Xd06wePqtfYow0ufdHTcumQte9Ls11hgZhDSMd7g6P9tPL5P0UeWa3x8xjNk6Lx3ghAVfzF2CePkeg4VQSHMonZEhaIxShdkCl3Z30F4DOe0p6Pq6ZsG6RRA4/qoIYGPvchfAC4L+VaWjC7yQ0k=
+ file: releases/*
+ skip_cleanup: true
+ prerelease: true # TODO: remove on 1.0
+ on:
+ repo: SUPERAndroidAnalyzer/super
+ tags: true
+ rust: stable
+ os: linux
notifications:
email:
recipients:
- - razican@protonmail.ch
- - brunoop@protonmail.ch
- - sergiodlo@protonmail.com
- - jaimesr@protonmail.ch
+ - razican@protonmail.ch
+ - brunoop@protonmail.ch
+ - sergiodlo@protonmail.com
+ - jaimesr@protonmail.ch
on_success: change
on_failure: always
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ea415d80..1ee480e0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,64 @@
# Changelog
+## SUPER 0.5.0
+
+This release contains multiple improvements that have been accumulated in the last year. We also
+improved our repository by adding a Code of Conduct and templates for issues and pull requests.
+Packages for Ubuntu, Debian, Fedora and CentOS are now generated automatically in each build, and
+deployed in each release. Here you can find the rest of the changes for this version.
+
+### Features
+
+- Added SDK version strings to Android versions. Reports will now show the Android version of the
+ target and minimum SDKs.
+- SUPER logo is now an SVG, so that it looks great in multiple resolutions.
+- All icons in the source tree viewer are now SVGs too.
+
+### Internal Changes
+
+- SUPER now requires Rust 1.30.0 to be built.
+- Removed `error-chain` dependency in favor of `failure`.
+- Upgraded dependencies:
+ - `clap`: 2.25 => **2.32**
+ - `xml-rs`: 0.4 => **0.8**
+ - `serde`: 0.9 => **1.0**
+ - `chrono`: 0.3 => **0.4**
+ - `toml`: 0.3 => **0.4**
+ - `regex`: 0.2 => **1.0**
+ - `lazy_static`: 0.2 => **1.1**
+ - `bytecount`: 0.1 => **0.4**
+ - `log`: 0.3 => **0.4**
+ - `env_logger`: 0.4 => **0.5**
+ - `sha1`: 0.2 => **0.6**
+ - `sha2`: 0.5 => **0.8**
+ - `abxml`: 0.2 => **0.6**
+ - `handlebars`: 0.25 => **1.1**
+ - Some other minor upgrades.
+- New dependencies:
+ - `failure`: 0.1
+ - `semver`: 0.9
+ - `hex`: 0.3
+ - `num_cpus`: 1.8
+- Multiple documentation improvements.
+- Code quality improved by using new syntax.
+- Fixed multiple performance bottlenecks.
+- Switched to library/binary architechture.
+
+### Bug Fixes
+
+- Fixed decompilation of badly formatted APK files.
+- Fixed strange characters in Windows Console.
+
+## SUPER 0.4.1
+
+### Internal Changes
+
+- Upgraded `abxml` to **0.2.0**.
+
+### Bug Fixes
+
+- SUPER now properly creates `dist` and `results` directories if they do not exist.
+
## SUPER 0.4.0
### Features
@@ -12,12 +71,12 @@
### Internal Changes
-- SUPER now requires Rust 1.16.0.
+- SUPER now requires Rust 1.16.0 to be built.
- Errors moved to their own module.
- Upgraded dependencies:
- `clap`: 2.20 => **2.23**
- `xml-rs`: 0.3 => **0.4**
- And some other minor upgrades.
+ - And some other minor upgrades.
- Dependency in `yaml-rust` has been removed.
- Dependency in `error-chain` 0.10 has been added.
- Dependency in `rust-crypto` has been removed and dependencies in `md5`, `sha1` and `sha2` have
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..3296cd9d7
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@superanalyzer.rocks. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/Cargo.lock b/Cargo.lock
index b8455f8ba..179c72dc7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,201 +1,299 @@
-[root]
-name = "super-analyzer"
-version = "0.4.1"
-dependencies = [
- "abxml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "colored 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "handlebars 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "md5 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "sha2 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "xml-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
[[package]]
name = "abxml"
-version = "0.2.0"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "xml-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "zip 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "adler32"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "aho-corasick"
-version = "0.6.3"
+version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
-version = "0.9.0"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "arrayvec"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "atty"
-version = "0.2.2"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
-version = "0.3.2"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
-version = "0.1.11"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "base64"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "block-buffer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "block-buffer"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "bitflags"
-version = "0.9.1"
+name = "block-padding"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "build_const"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byte-tools"
-version = "0.1.3"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "byte-tools"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytecount"
-version = "0.1.6"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
-version = "1.0.0"
+version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "bytes"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "bzip2"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "bzip2-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bzip2-sys"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "cc"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "cfg-if"
-version = "0.1.1"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chrono"
-version = "0.3.0"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap"
-version = "2.25.0"
+version = "2.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "colored"
-version = "1.5.1"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "dbghelp-sys"
-version = "0.2.0"
+name = "core-foundation"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "digest"
+name = "core-foundation-sys"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crc"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "generic-array 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "digest-buffer"
-version = "0.3.1"
+name = "crossbeam-utils"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "digest"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "digest"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "byte-tools 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "generic-array 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dtoa"
-version = "0.4.1"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -256,347 +354,1302 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "env_logger"
-version = "0.4.3"
+name = "encoding_rs"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "error-chain"
-version = "0.10.0"
+name = "env_logger"
+version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "fake-simd"
-version = "0.1.2"
+name = "failure"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "flate2"
-version = "0.2.19"
+name = "failure_derive"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "miniz-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "gcc"
-version = "0.3.51"
+name = "fake-simd"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "generic-array"
-version = "0.7.2"
+name = "flate2"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "typenum 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "handlebars"
-version = "0.25.3"
+name = "fnv"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "quick-error 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "itoa"
-version = "0.3.1"
+name = "foreign-types-shared"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
+name = "fuchsia-zircon"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "lazy_static"
-version = "0.2.8"
+name = "fuchsia-zircon-sys"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "libc"
-version = "0.2.24"
+name = "futures"
+version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "log"
-version = "0.3.8"
+name = "futures-cpupool"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "md5"
-version = "0.3.5"
+name = "generic-array"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "memchr"
-version = "1.0.1"
+name = "generic-array"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "miniz-sys"
-version = "0.1.9"
+name = "h2"
+version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "msdos_time"
-version = "0.1.5"
+name = "handlebars"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "nodrop"
-version = "0.1.9"
+name = "hex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "http"
+version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "num"
-version = "0.1.39"
+name = "httparse"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "humantime"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "num-integer"
-version = "0.1.34"
+name = "hyper"
+version = "0.12.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "num-iter"
-version = "0.1.33"
+name = "hyper-tls"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "num-traits"
-version = "0.1.39"
+name = "idna"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "odds"
-version = "0.2.25"
+name = "indexmap"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "open"
-version = "1.2.0"
+name = "iovec"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "pest"
-version = "0.3.3"
+name = "itoa"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "podio"
-version = "0.1.5"
+name = "kernel32-sys"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "quick-error"
+name = "lazy_static"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazycell"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "redox_syscall"
+name = "libc"
+version = "0.2.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libflate"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "regex"
-version = "0.2.2"
+name = "lock_api"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "regex-syntax"
-version = "0.4.1"
+name = "log"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "rustc-demangle"
-version = "0.1.4"
+name = "maplit"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "rustc-serialize"
-version = "0.3.24"
+name = "matches"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "serde"
-version = "0.9.15"
+name = "md5"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "serde_json"
-version = "0.9.10"
+name = "memchr"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "sha1"
-version = "0.2.0"
+name = "memoffset"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "sha2"
-version = "0.5.3"
+name = "mime"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "byte-tools 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "digest 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "digest-buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "generic-array 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "strsim"
-version = "0.6.0"
+name = "mime_guess"
+version = "2.0.0-alpha.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "term_size"
-version = "0.3.0"
+name = "miniz_oxide"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "textwrap"
-version = "0.6.0"
+name = "miniz_oxide_c_api"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "thread-id"
-version = "3.1.0"
+name = "mio"
+version = "0.6.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "thread_local"
-version = "0.3.3"
+name = "mio-uds"
+version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "time"
-version = "0.1.37"
+name = "miow"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "toml"
-version = "0.3.2"
+name = "msdos_time"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
-name = "typenum"
-version = "1.5.2"
+name = "native-tls"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num-integer"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num_cpus"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "opaque-debug"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "open"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl"
+version = "0.10.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "owning_ref"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "pest"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf"
+version = "0.7.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.7.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.7.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.7.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "podio"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "proc-macro2"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "quote"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_syscall"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_termios"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ucd-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.8.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libflate 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ryu"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "safemem"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "same-file"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scopeguard"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "security-framework"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "sha-1"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "sha1"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "sha2"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "unicode-segmentation"
-version = "1.1.0"
+name = "slab"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "unicode-width"
+name = "smallvec"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "string"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "strsim"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "super-analyzer"
+version = "0.5.0"
+dependencies = [
+ "abxml 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytecount 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "md5 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "open 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "0.14.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "0.15.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "termion"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "time"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-uds 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-codec"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-current-thread"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-executor"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-fs"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
[[package]]
-name = "unreachable"
+name = "tokio-io"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-reactor"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-tcp"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-threadpool"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-timer"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-udp"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tokio-uds"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "toml"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "typenum"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ucd-trie"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "ucd-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicase"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicase"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unreachable"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "url"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "utf8-ranges"
-version = "1.0.0"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "uuid"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
-version = "0.8.0"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "version_check"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -604,58 +1657,136 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "walkdir"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "want"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "winapi"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "xml-rs"
-version = "0.4.1"
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wincolor"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "xml-rs"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "zip"
-version = "0.2.3"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flate2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
-"checksum abxml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "414440395563bb56b4f38ae9aac160cab086492b42344a8f657dda0c46160867"
-"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
-"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
-"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
-"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76"
-"checksum backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0d842ea781ce92be2bf78a9b38883948542749640b8378b3b2f03d1fd9f1ff"
-"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
-"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
-"checksum byte-tools 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0919189ba800c7ffe8778278116b7e0de3905ab81c72abb69c85cbfef7991279"
-"checksum bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f09fbc8c6726a4b616dcfbd4f54491068d6bb1b93ac03c78ac18ff9a5924a"
-"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
-"checksum bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3eafc42c44e0d827de6b1c131175098fe7fb53b8ce8a47e65cb3ea94688be24"
-"checksum bzip2-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "98ce3fff84d4e90011f464bbdf48e3428f04270439f703868fd489d2aaedfc30"
-"checksum cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c47d456a36ebf0536a6705c83c1cbbcb9255fbc1d905a6ded104f479268a29"
-"checksum chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "158b0bd7d75cbb6bf9c25967a48a2e9f77da95876b858eadfabaa99cd069de6e"
-"checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771"
-"checksum colored 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66e436adfcba3a50905f943d990f5ef7c230ebe2d9476ab5c7dc2427afe357ee"
-"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
-"checksum digest 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a68d759d7a66a4f63d5bd2a2b14ad7e8cf93fe8c9be227031cd4e72ab0e9ee8"
-"checksum digest-buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4eb92364e9f6d3da159257250532d448b218406d2acb149f724e8f48e9f5cb9a"
-"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
+"checksum abxml 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53f926ba5386869607b4cdd8cb56ad374a2a38a03e954ababe6753614aba20e9"
+"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
+"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
+"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
+"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
+"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
+"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
+"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
+"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
+"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
+"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
+"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d"
+"checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3"
+"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
+"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
+"checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182"
+"checksum bytecount 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b92204551573580e078dc80017f36a213eb77a0450e4ddd8cfa0f3f2d1f0178f"
+"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
+"checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62"
+"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
+"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
+"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
+"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
+"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
+"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
+"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+"checksum colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc0a60679001b62fb628c4da80e574b9645ab4646056d7c9018885efffe45533"
+"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980"
+"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa"
+"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
+"checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1"
+"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9"
+"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
+"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
+"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
+"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
@@ -663,56 +1794,163 @@ dependencies = [
"checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
"checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
"checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
-"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
-"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8"
+"checksum encoding_rs 0.8.10 (registry+https://github.com/rust-lang/crates.io-index)" = "065f4d0c826fdaef059ac45487169d918558e3cf86c9d89f6e81cf52369126e5"
+"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
+"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
+"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
-"checksum flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)" = "36df0166e856739905cd3d7e0b210fe818592211a008862599845e012d8d304c"
-"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a"
-"checksum generic-array 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "330920f60726e8a1ca0129a40f0f0df0b8ee773945bf34895d578f35f31dc660"
-"checksum handlebars 0.25.3 (registry+https://github.com/rust-lang/crates.io-index)" = "15bdf598fc3c2de40c6b340213028301c0d225eea55a2294e6cc148074e557a1"
-"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
+"checksum flate2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3b0c7353385f92079524de3b7116cf99d73947c08a7472774e9b3b04bff3b901"
+"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
+"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
+"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
+"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
+"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
+"checksum h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd33bafe2e6370e6c8eb0cf1b8c5f93390b90acde7e9b03723f166b28b648ed"
+"checksum handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d82e5750d8027a97b9640e3fefa66bbaf852a35228e1c90790efd13c4b09c166"
+"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
+"checksum http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "24f58e8c2d8e886055c3ead7b28793e1455270b5fb39650984c224bc538ba581"
+"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"
+"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
+"checksum hyper 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)" = "95ffee0d1d30de4313fdaaa485891ce924991d45bbc18adfc8ac5b1639e62fbb"
+"checksum hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd73f14ad370d3b4d4b7dce08f69b81536c82e39fcc89731930fe5788cd661"
+"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
+"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
+"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
+"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
-"checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc"
-"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
-"checksum md5 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "11f2f3240857d306c26118e2e92a5eaf8990df379e3d96573ee6c92cdbf58a81"
-"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
-"checksum miniz-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "28eaee17666671fa872e567547e8428e83308ebe5808cdf6a0e28397dbe2c726"
-"checksum msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958"
-"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2"
-"checksum num 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "2c3a3dc9f30bf824141521b30c908a859ab190b76e20435fcd89f35eb6583887"
-"checksum num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1a4bf6f9174aa5783a9b4cc892cacd11aebad6c69ad027a0b65c6ca5f8aa37"
-"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e"
-"checksum num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "1708c0628602a98b52fad936cf3edb9a107af06e52e49fdf0707e884456a6af6"
-"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
-"checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842"
-"checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8"
-"checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
-"checksum quick-error 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c36987d4978eb1be2e422b1e0423a557923a5c3e7e6f31d5699e9aafaefa469"
-"checksum redox_syscall 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "3041aeb6000db123d2c9c751433f526e1f404b23213bd733167ab770c3989b4d"
-"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
-"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
-"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
-"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
-"checksum serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af"
-"checksum serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ad8bcf487be7d2e15d3d543f04312de991d631cfe1b43ea0ade69e6a8a5b16a1"
-"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
-"checksum sha2 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "84920f9ac881e94e33ec89e1b3dcd36040523a308a92548e01217ce35d8cf6a8"
-"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
-"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
-"checksum textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f86300c3e7416ee233abd7cda890c492007a3980f941f79185c753a701257167"
-"checksum thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8df7875b676fddfadffd96deea3b1124e5ede707d4884248931077518cf1f773"
-"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
-"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3"
-"checksum toml 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd86ad9ebee246fdedd610e0f6d0587b754a3d81438db930a244d0480ed7878f"
-"checksum typenum 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7242a7857c31d13620847d78af39ecac8d6c90aac23286e84aefe624c77c9c14"
-"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
-"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
-"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
-"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
-"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
+"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
+"checksum lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0"
+"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
+"checksum libflate 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "21138fc6669f438ed7ae3559d5789a5f0ba32f28c1f0608d1e452b0bb06ee936"
+"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a"
+"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
+"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43"
+"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+"checksum md5 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1ad8b18d0b6ae54e03c9fe1f7dea2ee5f8e0115a87611316794be1bc51537f7"
+"checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
+"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
+"checksum mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0a907b83e7b9e987032439a387e187119cddafc92d5c2aaeb1d92580a793f630"
+"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed"
+"checksum miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad30a47319c16cde58d0314f5d98202a80c9083b5f61178457403dfb14e509c"
+"checksum miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28edaef377517fd9fe3e085c37d892ce7acd1fbeab9239c5a36eec352d8a8b7e"
+"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432"
+"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
+"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
+"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2"
+"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
+"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
+"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
+"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
+"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
+"checksum opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51ecbcb821e1bd256d456fe858aaa7f380b63863eab2eb86eee1bd9f33dd6682"
+"checksum open 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eedfa0ca7b54d84d948bfd058b8f82e767d11f362dd78c36866fd1f69c175867"
+"checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613"
+"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+"checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106"
+"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
+"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
+"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
+"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
+"checksum pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a677051ad923732bb5c70f2d45f8985a96e3eee2e2bff86697e3b11b0c3fcfde"
+"checksum pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b76f477146419bc539a63f4ef40e902166cb43b3e51cecc71d9136fd12c567e7"
+"checksum pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebee4e9680be4fd162e6f3394ae4192a6b60b1e4d17d845e631f0c68d1a3386"
+"checksum pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f6d5f6f0e6082578c86af197d780dc38328e3f768cec06aac9bc46d714e8221"
+"checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6"
+"checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad"
+"checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b"
+"checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93"
+"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
+"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
+"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
+"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
+"checksum quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5829244f52738cfee93b3a165c1911388675be000c888d2fae620dee8fa5b"
+"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
+"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
+"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
+"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
+"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
+"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
+"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
+"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
+"checksum reqwest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "00a5870d8edc74fc6e1eb58edbd2815d2243e1a2255d6bf9c82a7a875901b5db"
+"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
+"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
+"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
+"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
+"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56"
+"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
+"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0"
+"checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf"
+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef"
+"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c"
+"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce"
+"checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650"
+"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded"
+"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
+"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
+"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d"
+"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d"
+"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
+"checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970"
+"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
+"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
+"checksum syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)" = "90c39a061e2f412a9f869540471ab679e85e50c6b05604daf28bc3060f75c430"
+"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
+"checksum tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "55c1195ef8513f3273d55ff59fe5da6940287a0d7a98331254397f464833675b"
+"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
+"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
+"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
+"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
+"checksum tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6e93c78d23cc61aa245a8acd2c4a79c4d7fa7fb5c3ca90d5737029f043a84895"
+"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f"
+"checksum tokio-current-thread 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f90fcd90952f0a496d438a976afba8e5c205fb12123f813d8ab3aa1c8436638c"
+"checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde"
+"checksum tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "60ae25f6b17d25116d2cba342083abe5255d3c2c79cb21ea11aa049c53bf7c75"
+"checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21"
+"checksum tokio-reactor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4b26fd37f1125738b2170c80b551f69ff6fecb277e6e5ca885e53eec2b005018"
+"checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912"
+"checksum tokio-threadpool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3929aee321c9220ed838ed6c3928be7f9b69986b0e3c22c972a66dbf8a298c68"
+"checksum tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3a52f00c97fedb6d535d27f65cccb7181c8dd4c6edc3eda9ea93f6d45d05168e"
+"checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c"
+"checksum tokio-uds 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df195376b43508f01570bacc73e13a1de0854dc59e79d1ec09913e8db6dd2a70"
+"checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65"
+"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
+"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
+"checksum ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77"
+"checksum ucd-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f8bfa9ff0cadcd210129ad9d2c5f145c13e9ced3d3e5d948a6213487d52444"
+"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
+"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90"
+"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
+"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
+"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
+"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
+"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
+"checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6"
+"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
+"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
+"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+"checksum walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ffb549f212c31e19f3667c55a7f515b983a84aef10fd0a4d1f9c125425115f3"
+"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-"checksum xml-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b46ee689ba7a669c08a1170c2348d2516c62dc461135c9e86b2f1f476e07be4a"
-"checksum zip 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a8e9988af1aa47bb7ccb1a61fd1261c45f646dda65ea00c6562d6b611403acf9"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
+"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"
+"checksum zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822"
diff --git a/Cargo.toml b/Cargo.toml
index 5e7fa0870..aa079556e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "super-analyzer"
-version = "0.4.1"
+version = "0.5.0"
authors = [
"Iban Eguia ",
"Jaime Salas ",
@@ -9,61 +9,73 @@ authors = [
license = "GPL-3.0"
readme = "README.md"
repository = "https://github.com/SUPERAndroidAnalyzer/super"
-homepage = "http://superanalyzer.rocks"
+homepage = "https://superanalyzer.rocks"
description = """\
Secure, Unified, Powerful and Extensible Rust Android Analyzer. Core software\
with CLI.\
"""
keywords = ["Android", "security", "audit", "super", "analyzer"]
categories = ["command-line-utilities", "development-tools"]
-build = "build.rs"
[badges]
travis-ci = { repository = "SUPERAndroidAnalyzer/super", branch = "master" }
appveyor = { repository = "Razican/super", branch = "master", service = "github" }
+codecov = { repository = "SUPERAndroidAnalyzer/super", branch = "master", service = "github" }
+is-it-maintained-issue-resolution = { repository = "SUPERAndroidAnalyzer/super" }
+is-it-maintained-open-issues = { repository = "SUPERAndroidAnalyzer/super" }
+maintenance = { status = "experimental" }
[[bin]]
-name = "super"
+name = "super-analyzer"
+path = "src/main.rs"
+
+[lib]
+name = "super_analyzer_core"
+path = "src/lib.rs"
[dependencies]
-clap = "^2.23"
-colored = "^1.4"
-xml-rs = "0.4"
-serde = "^0.9"
-serde_json = "^0.9"
-chrono = { version = "^0.3", features = ["serde"] }
-toml = "^0.3"
-regex = "^0.2"
-lazy_static = "^0.2"
-rustc-serialize = "^0.3"
-open = "^1.2"
-bytecount = "^0.1"
-log = "^0.3"
-env_logger = "^0.4"
-error-chain = "^0.10"
-md5 = "^0.3"
-sha1 = "^0.2"
-sha2 = "^0.5"
-abxml = "^0.2"
-
-[dependencies.handlebars]
-version = "^0.25"
-features = ["serde_type"]
+failure = "0.1.3"
+failure_derive = "0.1.3"
+clap = "2.32.0"
+xml-rs = "0.8.0"
+serde = "1.0.80"
+serde_derive = "1.0.80"
+serde_json = "1.0.32"
+chrono = { version = "0.4.6", features = ["serde"] }
+toml = "0.4.8"
+regex = "1.0.5"
+lazy_static = "1.1.0"
+open = "1.2.2"
+bytecount = "0.4.0"
+log = "0.4.6"
+env_logger = "0.5.13"
+md5 = "0.5.0"
+sha1 = "0.6.0"
+sha2 = "0.8.0"
+abxml = "0.6.2"
+handlebars = "1.1.0"
+semver = "0.9.0"
+hex = "0.3.2"
+num_cpus = "1.8.0"
+colored = "1.6.1"
[build-dependencies]
-clap = "^2.23"
+clap = "2.32.0"
+
+[dev-dependencies]
+reqwest = "0.9.4"
[features]
-default = ["beta"]
-beta = []
-unstable = ["beta", "certificate"]
+default = []
+unstable = ["certificate"]
+no-color = ["colored/no-color"]
certificate = []
[package.metadata.deb]
-maintainer = "Iban Eguia "
-copyright = "2016, Iban Eguia "
-license_file = ["LICENSE", "0"]
-extended_description = """\
+maintainer = "SUPER Team "
+copyright = "2016 - 2018, SUPER Team "
+license-file = ["LICENSE", "0"]
+extended-description = """\
Secure, Unified, Powerful and Extensible Rust Android Analyzer. Core software\
with CLI."""
depends = "libc6, libgcc1, default-jre-headless, bash"
@@ -71,73 +83,59 @@ section = "devel"
priority = "optional"
assets = [
# Executable
- ["target/release/super", "usr/bin/", "755"],
+ ["target/release/super-analyzer", "usr/bin/", "755"],
+
# Completion scripts
- ["target/release/super.bash-completion", "usr/share/bash-completion/completions/", "755"],
- ["target/release/super.fish", "usr/share/fish/vendor_completions.d/", "755"],
- ["target/release/_super", "usr/share/zsh/vendor-completions/", "755"],
+ #["target/release/super.bash", "usr/share/bash-completion/completions/", "755"],
+ #["target/release/super.fish", "usr/share/fish/vendor_completions.d/", "755"],
+ #["target/release/_super", "usr/share/zsh/vendor-completions/", "755"],
+
# JD-CMD
["vendor/jd-cmd.jar", "usr/share/super-analyzer/vendor/", "755"],
["vendor/jd-cmd.LICENSE.txt", "usr/share/super-analyzer/vendor/", "644"],
+
# Dex2Jar
["vendor/dex2jar-2.1-SNAPSHOT/LICENSE.txt", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/", "644"],
- ["vendor/dex2jar-2.1-SNAPSHOT/NOTICE.txt", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/", "644"],
- ["vendor/dex2jar-2.1-SNAPSHOT/d2j-dex2jar.sh", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/d2j_invoke.sh", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/antlr-runtime-3.5.2.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/asm-debug-all-4.1.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/d2j-base-cmd-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/d2j-jasmin-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/d2j-smali-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dex-ir-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dex-reader-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dex-reader-api-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dex-tools-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dex-translator-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dex-writer-2.1-SNAPSHOT.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- ["vendor/dex2jar-2.1-SNAPSHOT/lib/dx-1.7.jar", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
- #CSS
- ["templates/super/css/androidstudio.css", "usr/share/super-analyzer/templates/super/css/", "644"],
- ["templates/super/css/style.css", "usr/share/super-analyzer/templates/super/css/", "644"],
- # Images
- ["templates/super/img/folder-icon.png", "usr/share/super-analyzer/templates/super/img/", "644"],
- ["templates/super/img/java-icon.png", "usr/share/super-analyzer/templates/super/img/", "644"],
- ["templates/super/img/xml-icon.png", "usr/share/super-analyzer/templates/super/img/", "644"],
- ["templates/super/img/report.png", "usr/share/super-analyzer/vendor/results_template/img/", "644"],
- ["templates/super/img/logo.png", "usr/share/super-analyzer/templates/super/img/", "644"],
- # JavaScript
- ["templates/super/js/highlight.js.LICENSE", "usr/share/super-analyzer/templates/super/js/", "644"],
- ["templates/super/js/highlight.pack.js", "usr/share/super-analyzer/templates/super/js/", "644"],
- ["templates/super/js/jquery-3.2.0.slim.min.js", "usr/share/super-analyzer/templates/super/js/", "644"],
- ["templates/super/js/src_nav.js", "usr/share/super-analyzer/templates/super/js/", "644"],
- # templates
- ["templates/super/code.hbs", "usr/share/super-analyzer/templates/super/", "644"],
- ["templates/super/report.hbs", "usr/share/super-analyzer/templates/super/", "644"],
- ["templates/super/src.hbs", "usr/share/super-analyzer/templates/super/", "644"],
- ["templates/super/vulnerability.hbs", "usr/share/super-analyzer/templates/super/", "644"],
- # Config
+ ["vendor/dex2jar-2.1-SNAPSHOT/*.sh", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/", "755"],
+ ["vendor/dex2jar-2.1-SNAPSHOT/lib/*", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/lib/", "755"],
+ ["vendor/dex2jar-2.1-SNAPSHOT/bin/dex-tools", "usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT/bin/", "755"],
+
+ # Web assets
+ ["templates/super/css/*", "usr/share/super-analyzer/templates/super/css/", "644"],
+ ["templates/super/img/*", "usr/share/super-analyzer/templates/super/img/", "644"],
+ ["templates/super/js/*", "usr/share/super-analyzer/templates/super/js/", "644"],
+
+ # Templates
+ ["templates/super/*", "usr/share/super-analyzer/templates/super/", "644"],
+
+ # Configuration
["rules.json", "etc/super-analyzer/", "644"],
["config.toml", "etc/super-analyzer/", "644"],
["config.toml.sample", "etc/super-analyzer/", "644"],
+
# README
["README.md", "usr/share/doc/super-analyzer/README.md", "644"],
]
# The release profile, used for `cargo build`.
[profile.dev]
+incremental = true
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
-codegen-units = 1
+overflow-checks = true
panic = 'unwind'
# The release profile, used for `cargo build --release`.
[profile.release]
+incremental = false
opt-level = 3
debug = false
rpath = false
+codegen-units = 1
lto = true
debug-assertions = false
+overflow-checks = false
panic = 'unwind'
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..b96d15413
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,10 @@
+**Description**
+A clear and concise description of what the pull request is about.
+If it's a new feature, explain what it does, if it's a fix, explain what it fixes.
+
+**Related issues**
+Mention any issue that this pull request might fix or could be related to. You can use the `#123` notation.
+
+**Ping contributors**
+Mention any relevant contributor to the project using the `@` notation, such as `@Razican`.
+If you don't know who to mention, `@SUPERAndroidAnalyzer/all` is a safe bet.
diff --git a/README.md b/README.md
index 67fa279e2..f630ce660 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[![Build status][windows_build_img]][windows_build]
[![codecov][coverage_img]][coverage]
-
+
*Secure, Unified, Powerful and Extensible Rust Android Analyzer*
@@ -75,7 +75,7 @@ OPTIONS:
--results Folder where to store the results
--rules Path to a JSON rules file
--template Path to a results template file
- -t, --threads Number of threads to use
+ -t, --threads Number of threads to use, by default it will use one thread per logical CPU core
ARGS:
The package string of the application to test
@@ -86,6 +86,8 @@ ARGS:
Everybody is welcome to contribute to SUPER. Please check out the
[SUPER Contribution Guidelines][contributing] for instructions about how to proceed.
+[Development documentation][documentation]
+
## License ##
This program is free software: you can redistribute it and/or modify it under the terms of the GNU
@@ -102,3 +104,4 @@ License, or (at your option) any later version.
[downloads]: http://superanalyzer.rocks/download.html
[rustup]: https://www.rustup.rs/
[contributing]: https://github.com/SUPERAndroidAnalyzer/super/blob/master/contributing.md
+[documentation]: https://superandroidanalyzer.github.io/super/
diff --git a/build.rs b/build.rs
index 2482d9252..4b036029c 100644
--- a/build.rs
+++ b/build.rs
@@ -1,16 +1,17 @@
-#[macro_use]
extern crate clap;
-use std::path::PathBuf;
-use std::env;
use clap::Shell;
+use std::env;
+use std::path::PathBuf;
#[path = "src/cli.rs"]
mod cli;
fn main() {
let mut cli = cli::generate();
- let mut out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+ let mut out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR variable was not set"));
+
+ // This will output the completions in the target/release or target/debug directories.
out_dir.pop();
out_dir.pop();
out_dir.pop();
diff --git a/centos_build.sh b/centos_build.sh
new file mode 100755
index 000000000..63e03a211
--- /dev/null
+++ b/centos_build.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Update the system
+yum upgrade -y &&
+yum autoremove -y &&
+
+# Install build dependencies
+yum install -y wget gcc make rpm-build redhat-rpm-config &&
+
+# Create needed directories and macros
+mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} &&
+echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros &&
+
+# Create the package
+cd /root &&
+mkdir super-analyzer-$TAG &&
+cp -r super/* super-analyzer-$TAG/ &&
+rm -fr super-analyzer-$TAG/target super-analyzer-$TAG/rpmbuild super-analyzer-$TAG/.git super-analyzer-$TAG/dist super-analyzer-$TAG/downloads super-analyzer-$TAG/results &&
+tar -czvf /root/rpmbuild/SOURCES/$TAG.tar.gz super-analyzer-$TAG &&
+
+# Build the RPM
+cp /root/super/rpmbuild/super.spec /root/rpmbuild/SPECS/ &&
+rpmbuild -v -bb /root/rpmbuild/SPECS/super.spec &&
+mv /root/rpmbuild/RPMS/x86_64/super-analyzer-$TAG-1.el7.x86_64.rpm /root/super/releases/
diff --git a/clippy.toml b/clippy.toml
index be7fe9315..59ef48d25 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -1,4 +1,4 @@
-cyclomatic-complexity-threshold = 20
+cyclomatic-complexity-threshold = 25
single-char-binding-names-threshold = 4
too-many-arguments-threshold = 7
-type-complexity-threshold = 125
+type-complexity-threshold = 150
diff --git a/config.toml.sample b/config.toml.sample
index e1f97cfdb..52470122c 100644
--- a/config.toml.sample
+++ b/config.toml.sample
@@ -9,8 +9,7 @@ template = "super" # Results template
rules_json = "/etc/super-analyzer/rules.json" # Vulnerability rules JSON
# Vulnerable or potentially vulnerable permissions
-[[permissions]]
-name = "unknown" # Unknown permissions
+[unknown_permissions]
criticality = "low"
description = "Even if the application can create its own permissions, it's discouraged, since it can lead to missunderstanding between developers."
diff --git a/debian_build.sh b/debian_build.sh
new file mode 100755
index 000000000..0db9b0cff
--- /dev/null
+++ b/debian_build.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Update the system
+apt-get update -y &&
+apt-get upgrade -y &&
+apt-get dist-upgrade -y &&
+apt-get autoremove -y &&
+
+# Install build dependencies
+apt-get install -y curl build-essential dpkg libc-bin liblzma-dev &&
+
+# Install Rust
+curl https://sh.rustup.rs -sSf | sh -s -- -y &&
+source ~/.cargo/env &&
+
+# Install cargo-deb
+cargo install cargo-deb &&
+
+# Generate the .deb file
+cd /root/super &&
+cargo deb &&
+mv target/debian/super-analyzer_`echo $TAG`_amd64.deb releases/super-analyzer_`echo $TAG`_debian_amd64.deb
diff --git a/fedora_build.sh b/fedora_build.sh
new file mode 100755
index 000000000..79aa1272e
--- /dev/null
+++ b/fedora_build.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Update the system
+dnf upgrade --refresh -y &&
+dnf autoremove -y &&
+
+# Install build dependencies
+dnf install -y wget gcc fedora-packager &&
+
+# Create the package
+cd /root &&
+mkdir super-analyzer-$TAG &&
+cp -r super/* super-analyzer-$TAG/ &&
+rm -fr super-analyzer-$TAG/target super-analyzer-$TAG/rpmbuild super-analyzer-$TAG/.git super-analyzer-$TAG/dist super-analyzer-$TAG/downloads super-analyzer-$TAG/results &&
+tar -czvf super/rpmbuild/$TAG.tar.gz super-analyzer-$TAG &&
+
+# Build the RPM
+cd /root/super/rpmbuild &&
+fedpkg --release f28 local &&
+mv x86_64/super-analyzer-$TAG-1.fc28.x86_64.rpm ../releases/
diff --git a/rpmbuild/super.spec b/rpmbuild/super.spec
index ac69f7aed..72ecc1b73 100644
--- a/rpmbuild/super.spec
+++ b/rpmbuild/super.spec
@@ -1,8 +1,8 @@
Name: super-analyzer
-Version: 0.4.1
+Version: 0.5.0
Release: 1%{?dist}
Summary: Secure, Unified, Powerful and Extensible Rust Android Analyzer.
-URL: http://superanalyzer.rocks/
+URL: https://superanalyzer.rocks/
License: GPLv3+
Source0: https://github.com/SUPERAndroidAnalyzer/super/archive/%{version}.tar.gz
@@ -11,6 +11,8 @@ Requires: java-1.8.0-openjdk-headless, bash
%description
Secure, Unified, Powerful and Extensible Rust Android Analyzer.
+%global debug_package %{nil}
+
%prep
%autosetup
/usr/bin/curl https://sh.rustup.rs -sSf | sh -s -- -y
@@ -28,22 +30,24 @@ mkdir -p %{buildroot}%{_datadir}/%{name}/templates/super/css
mkdir -p %{buildroot}%{_datadir}/%{name}/templates/super/img
mkdir -p %{buildroot}%{_datadir}/%{name}/templates/super/js
mkdir -p %{buildroot}%{_datadir}/%{name}/vendor/dex2jar-2.1-SNAPSHOT/lib
+mkdir -p %{buildroot}%{_datadir}/%{name}/vendor/dex2jar-2.1-SNAPSHOT/bin
mkdir -p %{buildroot}%{_sysconfdir}/%{name}/
mkdir -p %{buildroot}%{_defaultdocdir}/%{name}/
install -p -d -m 755 %{buildroot}%{_datadir}/%{name}
-install -p -m 755 target/release/super %{buildroot}%{_bindir}/
-install -p -m 755 target/release/super.bash-completion %{buildroot}%{_datadir}/bash-completion/completions/
+install -p -m 755 target/release/%{name} %{buildroot}%{_bindir}/
+install -p -m 755 target/release/super.bash %{buildroot}%{_datadir}/bash-completion/completions/
install -p -m 755 target/release/super.fish %{buildroot}%{_datadir}/fish/vendor_completions.d/
install -p -m 755 target/release/_super %{buildroot}%{_datadir}/zsh/site-functions/
install -p -m 755 -D vendor/dex2jar-2.1-SNAPSHOT/lib/* %{buildroot}%{_datadir}/%{name}/vendor/dex2jar-2.1-SNAPSHOT/lib/
+install -p -m 755 -D vendor/dex2jar-2.1-SNAPSHOT/bin/dex-tools %{buildroot}%{_datadir}/%{name}/vendor/dex2jar-2.1-SNAPSHOT/bin/
install -p -m 755 -D vendor/dex2jar-2.1-SNAPSHOT/*.sh %{buildroot}%{_datadir}/%{name}/vendor/dex2jar-2.1-SNAPSHOT/
install -p -m 644 -D vendor/dex2jar-2.1-SNAPSHOT/LICENSE.txt %{buildroot}%{_datadir}/%{name}/vendor/dex2jar-2.1-SNAPSHOT/
install -p -m 644 -D templates/super/css/* %{buildroot}%{_datadir}/%{name}/templates/super/css/
install -p -m 644 -D templates/super/img/* %{buildroot}%{_datadir}/%{name}/templates/super/img/
install -p -m 644 -D templates/super/js/* %{buildroot}%{_datadir}/%{name}/templates/super/js/
install -p -m 644 -D templates/super/*.hbs %{buildroot}%{_datadir}/%{name}/templates/super/
-install -p -m 755 -D vendor/*.jar %{buildroot}%{_datadir}/%{name}/vendor/
-install -p -m 644 -D vendor/*.txt %{buildroot}%{_datadir}/%{name}/vendor/
+install -p -m 755 -D vendor/jd-cmd.jar %{buildroot}%{_datadir}/%{name}/vendor/
+install -p -m 644 -D vendor/jd-cmd.LICENSE.txt %{buildroot}%{_datadir}/%{name}/vendor/
install -p -m 644 rules.json %{buildroot}%{_sysconfdir}/%{name}/
install -p -m 644 config.toml %{buildroot}%{_sysconfdir}/%{name}/
install -p -m 644 config.toml.sample %{buildroot}%{_sysconfdir}/%{name}/
diff --git a/rules.json b/rules.json
index 73f780825..2d42c859c 100644
--- a/rules.json
+++ b/rules.json
@@ -53,7 +53,7 @@
"description": "Sensitive information should never be logged since it can lead to that information being disclosed.",
"include_file_regex": ".java$"
}, {
- "regex": "(?:(?:\\b[[:upper:]]{1}:)\\\\\\s*[^\\0 !$&*(?:)+]\\w.+)|(?:(?:\\b[[:upper:]]{1}:)\\\\)",
+ "regex": "(?:(?:\\b[[:upper:]]{1}:)\\\\\\s*[^0 !$&*(?:)+]\\w.+)|(?:(?:\\b[[:upper:]]{1}:)\\\\)",
"criticality": "warning",
"label": "Hardcoded file separator",
"description": "Paths like C:\\\\Program Files\\\\... can cause problems, and are considered vulnerabilities, since some OSs use backslashes `\\\\` (DOS\/Windows) and others slashes `\/` (Unix)."
diff --git a/src/cli.rs b/src/cli.rs
index c554c595f..8e7b1cab1 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,5 +1,12 @@
-use clap::{Arg, App};
+//! Command line interface module.
+//!
+//! This module contains the `generate()` function, that generates the complet command line
+//! for the SUPER launcher. It's also used to generate command line completion scripts in the
+//! `build.rs` file.
+use clap::{crate_version, App, Arg};
+
+/// Generates the command line interface.
pub fn generate() -> App<'static, 'static> {
App::new("SUPER Android Analyzer")
.version(crate_version!())
@@ -12,101 +19,90 @@ pub fn generate() -> App<'static, 'static> {
.required_unless("test-all")
.conflicts_with("test-all")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("test-all")
.short("a")
.long("test-all")
.conflicts_with("package")
.conflicts_with("open")
.help("Test all .apk files in the downloads directory"),
- )
- .arg(
+ ).arg(
Arg::with_name("verbose")
.short("v")
.long("verbose")
.conflicts_with("quiet")
.help("If you'd like the auditor to talk more than necessary"),
- )
- .arg(Arg::with_name("force").long("force").help(
- "If you'd like to force the auditor to do everything from the beginning",
- ))
- .arg(Arg::with_name("bench").long("bench").help(
- "Show benchmarks for the analysis",
- ))
- .arg(
+ ).arg(
+ Arg::with_name("force")
+ .long("force")
+ .help("If you'd like to force the auditor to do everything from the beginning"),
+ ).arg(
+ Arg::with_name("bench")
+ .long("bench")
+ .help("Show benchmarks for the analysis"),
+ ).arg(
Arg::with_name("quiet")
.short("q")
.long("quiet")
.conflicts_with("verbose")
- .help(
- "If you'd like a zen auditor that won't output anything in stdout",
- ),
- )
- .arg(
+ .help("If you'd like a zen auditor that won't output anything in stdout"),
+ ).arg(
Arg::with_name("open")
.long("open")
.conflicts_with("test-all")
.help("Open the report in a browser once it is complete"),
- )
- .arg(Arg::with_name("json").long("json").help(
- "Generates the reults in JSON format",
- ))
- .arg(Arg::with_name("html").long("html").help(
- "Generates the reults in HTML format",
- ))
- .arg(
+ ).arg(
+ Arg::with_name("json")
+ .long("json")
+ .help("Generates the reults in JSON format"),
+ ).arg(
+ Arg::with_name("html")
+ .long("html")
+ .help("Generates the reults in HTML format"),
+ ).arg(
Arg::with_name("min_criticality")
.long("min-criticality")
- .help(
- "Set a minimum criticality to analyze (Critical, High, Medium, Low)",
- )
+ .help("Set a minimum criticality to analyze (Critical, High, Medium, Low)")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("threads")
.short("t")
.long("threads")
- .help("Number of threads to use")
- .takes_value(true),
- )
- .arg(
+ .help(
+ "Number of threads to use, by default it will use one thread per logical CPU \
+ core",
+ ).takes_value(true),
+ ).arg(
Arg::with_name("downloads")
.long("downloads")
.help("Folder where the downloads are stored")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("dist")
.long("dist")
.help("Folder where distribution files will be extracted")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("results")
.long("results")
.help("Folder where to store the results")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("dex2jar")
.long("dex2jar")
.help("Where to store the jar files")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("jd-cmd")
.long("jd-cmd")
.help("Path to the jd-cmd file")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("template")
.long("template")
.help("Path to a results template file")
.takes_value(true),
- )
- .arg(
+ ).arg(
Arg::with_name("rules")
.long("rules")
.help("Path to a JSON rules file")
diff --git a/src/config.rs b/src/config.rs
index 5c76cda6f..ec9d198b4 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -2,34 +2,32 @@
//!
//! Handles and configures the initial settings and variables needed to run the program.
-use std::{u8, fs};
-use std::path::{Path, PathBuf};
-use std::convert::From;
-use std::str::FromStr;
-use std::io::Read;
-use std::collections::btree_set::Iter;
-use std::slice::Iter as VecIter;
-use std::collections::BTreeSet;
-use std::cmp::{PartialOrd, Ordering};
-use std::error::Error as StdError;
+use std::{
+ cmp::{Ordering, PartialOrd},
+ collections::{btree_set::Iter, BTreeSet},
+ convert::From,
+ fs, i64,
+ path::{Path, PathBuf},
+ slice::Iter as VecIter,
+ str::FromStr,
+ usize,
+};
-use colored::Colorize;
-use toml::Value;
use clap::ArgMatches;
+use colored::Colorize;
+use failure::{format_err, Error, ResultExt};
+use num_cpus;
+use serde::{de, Deserialize, Deserializer};
+use toml::{self, value::Value};
-use static_analysis::manifest::Permission;
-
-use error::*;
-use {Criticality, print_warning};
-
-/// Largest number of threads allowed.
-const MAX_THREADS: i64 = u8::MAX as i64;
+use crate::{criticality::Criticality, print_warning, static_analysis::manifest};
/// Config structure.
///
/// Contains configuration related fields. It is used for storing the configuration parameters and
/// checking their values. Implements the `Default` trait.
-#[derive(Debug)]
+#[derive(Debug, Deserialize)]
+#[serde(default)]
pub struct Config {
/// Application packages to analyze.
app_packages: Vec,
@@ -52,7 +50,8 @@ pub struct Config {
/// Minimum criticality to analyze
min_criticality: Criticality,
/// Number of threads.
- threads: u8,
+ #[serde(deserialize_with = "ConfigDeserializer::deserialize_threads")]
+ threads: usize,
/// Folder where the applications are stored.
downloads_folder: PathBuf,
/// Folder with files from analyzed applications.
@@ -70,67 +69,150 @@ pub struct Config {
/// The name of the template to use.
template: String,
/// Represents an unknow permission.
+ #[serde(deserialize_with = "ConfigDeserializer::deserialize_unknown_permission")]
unknown_permission: (Criticality, String),
/// List of permissions to analyze.
- permissions: BTreeSet,
+ permissions: BTreeSet,
/// Checker for the loaded files
loaded_files: Vec,
}
-impl Config {
- /// Creates a new `Config` struct.
- pub fn from_cli(cli: ArgMatches<'static>) -> Result {
- let mut config = Config::default();
-
- config.verbose = cli.is_present("verbose");
- config.quiet = cli.is_present("quiet");
- config.overall_force = cli.is_present("force");
- config.force = config.overall_force;
- config.bench = cli.is_present("bench");
- config.open = cli.is_present("open");
- config.json = cli.is_present("json");
- config.html = cli.is_present("html");
-
- if cfg!(target_family = "unix") {
- let config_path = PathBuf::from("/etc/config.toml");
- if config_path.exists() {
- config.load_from_file(&config_path).chain_err(
- || "The config.toml file does not have the correct formatting.",
- )?;
- config.loaded_files.push(config_path);
+/// Helper struct that handles some specific field deserialization for `Config` struct
+struct ConfigDeserializer;
+
+/// `Criticality` and `String` tuple, used to shorten some return types.
+type CriticalityString = (Criticality, String);
+
+impl ConfigDeserializer {
+ /// Deserialize `thread` field and checks that is on the proper bounds
+ pub fn deserialize_threads<'de, D>(de: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ let deser_result: Value = Deserialize::deserialize(de)?;
+
+ #[cfg_attr(feature = "cargo-clippy", allow(use_debug))]
+ match deser_result {
+ Value::Integer(threads) => {
+ if threads > 0 && threads <= {
+ // TODO: change it for compile-time check.
+ if (usize::max_value() as i64) < 0 {
+ // 64-bit machine
+ i64::max_value()
+ } else {
+ // Smaller than 64 bit words.
+ usize::max_value() as i64
+ }
+ } {
+ Ok(threads as usize)
+ } else {
+ Err(de::Error::custom("threads is not in the valid range"))
+ }
}
+ _ => Err(de::Error::custom(format!(
+ "unexpected value: {:?}",
+ deser_result
+ ))),
+ }
+ }
+
+ /// Deserialize `unknown_permission` field
+ pub fn deserialize_unknown_permission<'de, D>(de: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ let deser_result: Value = Deserialize::deserialize(de)?;
+
+ #[cfg_attr(feature = "cargo-clippy", allow(use_debug))]
+ match deser_result {
+ Value::Table(ref table) => {
+ let criticality_str = table
+ .get("criticality")
+ .and_then(|v| v.as_str())
+ .ok_or_else(|| {
+ de::Error::custom("criticality field not found for unknown permission")
+ })?;
+ let string = table
+ .get("description")
+ .and_then(|v| v.as_str())
+ .ok_or_else(|| {
+ de::Error::custom("description field not found for unknown permission")
+ })?;
+
+ let criticality = Criticality::from_str(criticality_str).map_err(|_| {
+ de::Error::custom(format!(
+ "invalid `criticality` value found: {}",
+ criticality_str
+ ))
+ })?;
+
+ Ok((criticality, string.to_string()))
+ }
+ _ => Err(de::Error::custom(format!(
+ "Unexpected value: {:?}",
+ deser_result
+ ))),
}
- let config_path = PathBuf::from("config.toml");
- if config_path.exists() {
- config.load_from_file(&config_path).chain_err(
- || "The config.toml file does not have the correct formatting.",
- )?;
- config.loaded_files.push(config_path);
- }
+ }
+}
- config.set_options(&cli);
+impl Config {
+ /// Creates a new `Config` struct.
+ pub fn from_file>(config_path: P) -> Result {
+ let cfg_result: Result = fs::read_to_string(config_path.as_ref())
+ .context("could not open configuration file")
+ .map_err(Error::from)
+ .and_then(|file_content| {
+ Ok(toml::from_str(&file_content).context(format_err!(
+ "could not decode config file: {}, using default",
+ config_path.as_ref().to_string_lossy()
+ ))?)
+ }).and_then(|mut new_config: Self| {
+ new_config
+ .loaded_files
+ .push(config_path.as_ref().to_path_buf());
+
+ Ok(new_config)
+ });
+
+ cfg_result
+ }
+
+ /// Decorates the loaded config with the given flags from CLI
+ pub fn decorate_with_cli(&mut self, cli: &ArgMatches<'static>) -> Result<(), Error> {
+ self.set_options(cli);
+
+ self.verbose = cli.is_present("verbose");
+ self.quiet = cli.is_present("quiet");
+ self.overall_force = cli.is_present("force");
+ self.force = self.overall_force;
+ self.bench = cli.is_present("bench");
+ self.open = cli.is_present("open");
+ self.json = cli.is_present("json");
+ self.html = cli.is_present("html");
if cli.is_present("test-all") {
- config.read_apks().chain_err(
- || "Error loading all the downloaded APKs",
- )?;
+ self.read_apks()
+ .context("error loading all the downloaded APKs")?;
} else {
- config.add_app_package(cli.value_of("package").unwrap());
+ self.add_app_package(
+ cli.value_of("package")
+ .expect("expected a value for the package CLI attribute"),
+ );
}
- Ok(config)
+ Ok(())
}
/// Modifies the options from the CLI.
fn set_options(&mut self, cli: &ArgMatches<'static>) {
if let Some(min_criticality) = cli.value_of("min_criticality") {
if let Ok(m) = min_criticality.parse() {
-
self.min_criticality = m;
} else {
print_warning(format!(
- "The min_criticality option must be one of {}, {}, {}, {} \
- or {}.\nUsing default.",
+ "The min_criticality option must be one of {}, {}, {}, {} or {}.\nUsing \
+ default.",
"warning".italic(),
"low".italic(),
"medium".italic(),
@@ -141,14 +223,13 @@ impl Config {
}
if let Some(threads) = cli.value_of("threads") {
match threads.parse() {
- Ok(t) if t > 0_u8 => {
+ Ok(t) if t > 0_usize => {
self.threads = t;
}
_ => {
print_warning(format!(
- "The threads option must be an integer between 1 and \
- {}",
- u8::MAX
+ "The threads option must be an integer between 1 and {}",
+ usize::max_value()
));
}
}
@@ -177,7 +258,7 @@ impl Config {
}
/// Reads all the apk files in the downloads folder and adds them to the configuration.
- fn read_apks(&mut self) -> Result<()> {
+ fn read_apks(&mut self) -> Result<(), Error> {
let iter = fs::read_dir(&self.downloads_folder)?;
for entry in iter {
@@ -189,7 +270,7 @@ impl Config {
entry
.path()
.file_stem()
- .unwrap()
+ .expect("expected file stem for apk file")
.to_string_lossy()
.into_owned(),
)
@@ -198,9 +279,8 @@ impl Config {
}
Err(e) => {
print_warning(format!(
- "There was an error when reading the \
- downloads folder: {}",
- e.description()
+ "there was an error when reading the downloads folder: {}",
+ e
));
}
}
@@ -211,9 +291,11 @@ impl Config {
/// Checks if all the needed folders and files exist.
pub fn check(&self) -> bool {
- let check = self.downloads_folder.exists() && self.dex2jar_folder.exists() &&
- self.jd_cmd_file.exists() && self.get_template_path().exists() &&
- self.rules_json.exists();
+ let check = self.downloads_folder.exists()
+ && self.dex2jar_folder.exists()
+ && self.jd_cmd_file.exists()
+ && self.template_path().exists()
+ && self.rules_json.exists();
if check {
for package in &self.app_packages {
if !package.exists() {
@@ -227,7 +309,7 @@ impl Config {
}
/// Returns the folders and files that do not exist.
- pub fn get_errors(&self) -> Vec {
+ pub fn errors(&self) -> Vec {
let mut errors = Vec::new();
if !self.downloads_folder.exists() {
errors.push(format!(
@@ -261,7 +343,7 @@ impl Config {
self.templates_folder.display()
));
}
- if !self.get_template_path().exists() {
+ if !self.template_path().exists() {
errors.push(format!(
"the template `{}` does not exist in `{}`",
self.template,
@@ -278,24 +360,32 @@ impl Config {
}
/// Returns the currently loaded config files.
- pub fn get_loaded_config_files(&self) -> VecIter {
+ pub fn loaded_config_files(&self) -> VecIter {
self.loaded_files.iter()
}
/// Returns the app package.
- pub fn get_app_packages(&self) -> Vec {
+ pub fn app_packages(&self) -> Vec {
self.app_packages.clone()
}
/// Adds a package to check.
- fn add_app_package>(&mut self, app_package: P) {
+ pub(crate) fn add_app_package>(&mut self, app_package: P) {
let mut package_path = self.downloads_folder.join(app_package);
if package_path.extension().is_none() {
- package_path.set_extension("apk");
- } else if package_path.extension().unwrap() != "apk" {
+ let updated = package_path.set_extension("apk");
+ debug_assert!(
+ updated,
+ "did not update package path extension, no file name"
+ );
+ } else if package_path
+ .extension()
+ .expect("expected extension in package path")
+ != "apk"
+ {
let mut file_name = package_path
.file_name()
- .unwrap()
+ .expect("expected file name in package path")
.to_string_lossy()
.into_owned();
file_name.push_str(".apk");
@@ -351,373 +441,73 @@ impl Config {
}
/// Returns the `min_criticality` field.
- pub fn get_min_criticality(&self) -> Criticality {
+ pub fn min_criticality(&self) -> Criticality {
self.min_criticality
}
/// Returns the `threads` field.
- pub fn get_threads(&self) -> u8 {
+ pub fn threads(&self) -> usize {
self.threads
}
/// Returns the path to the `dist_folder`.
- pub fn get_dist_folder(&self) -> &Path {
+ pub fn dist_folder(&self) -> &Path {
&self.dist_folder
}
/// Returns the path to the `results_folder`.
- pub fn get_results_folder(&self) -> &Path {
+ pub fn results_folder(&self) -> &Path {
&self.results_folder
}
/// Returns the path to the `dex2jar_folder`.
- pub fn get_dex2jar_folder(&self) -> &Path {
+ pub fn dex2jar_folder(&self) -> &Path {
&self.dex2jar_folder
}
/// Returns the path to the `jd_cmd_file`.
- pub fn get_jd_cmd_file(&self) -> &Path {
+ pub fn jd_cmd_file(&self) -> &Path {
&self.jd_cmd_file
}
/// Gets the path to the template.
- pub fn get_template_path(&self) -> PathBuf {
+ pub fn template_path(&self) -> PathBuf {
self.templates_folder.join(&self.template)
}
/// Gets the path to the templates folder.
- pub fn get_templates_folder(&self) -> &Path {
+ pub fn templates_folder(&self) -> &Path {
&self.templates_folder
}
/// Gets the name of the template.
- pub fn get_template_name(&self) -> &str {
+ pub fn template_name(&self) -> &str {
&self.template
}
/// Returns the path to the `rules_json`.
- pub fn get_rules_json(&self) -> &Path {
+ pub fn rules_json(&self) -> &Path {
&self.rules_json
}
/// Returns the criticality of the `unknown_permission` field.
- pub fn get_unknown_permission_criticality(&self) -> Criticality {
+ pub fn unknown_permission_criticality(&self) -> Criticality {
self.unknown_permission.0
}
/// Returns the description of the `unknown_permission` field.
- pub fn get_unknown_permission_description(&self) -> &str {
+ pub fn unknown_permission_description(&self) -> &str {
self.unknown_permission.1.as_str()
}
/// Returns the loaded `permissions`.
- pub fn get_permissions(&self) -> Iter {
+ pub fn permissions(&self) -> Iter {
self.permissions.iter()
}
- /// Loads a configuration file into the `Config` struct.
- fn load_from_file>(&mut self, path: P) -> Result<()> {
- let mut f = fs::File::open(path)?;
- let mut toml = String::new();
- let _ = f.read_to_string(&mut toml)?;
-
- // Parse the configuration file.
- let toml = if let Value::Table(toml) =
- toml.parse::().chain_err(
- || "there was an error parsing the config.toml file",
- )?
- {
- toml
- } else {
- return Err(ErrorKind::Parse.into());
- };
-
- // Read the values from the configuration file.
- for (key, value) in toml {
- match key.as_str() {
- "threads" => self.load_threads_section(value),
- "downloads_folder" => self.load_downloads_folder_section(value),
- "dist_folder" => self.load_dist_folder_section(value),
- "results_folder" => self.load_results_folder_section(value),
- "dex2jar_folder" => self.load_dex2jar_folder_section(value),
- "jd_cmd_file" => self.load_jd_cmd_file_section(value),
- "templates_folder" => self.load_templates_folder_section(value),
- "template" => self.load_template_section(value),
- "rules_json" => self.load_rules_section(value),
- "permissions" => self.load_permissions(value),
- "html_report" => self.load_html_report_section(value),
- "json_report" => self.load_json_report_section(value),
- _ => print_warning(format!("Unknown configuration option {}.", key)),
-
- }
- }
- Ok(())
- }
-
- /// Loads threads section from the TOML value.
- fn load_threads_section(&mut self, value: Value) {
- if let Value::Integer(t @ 1...MAX_THREADS) = value {
- #[allow(cast_sign_loss)]
- {
- self.threads = t as u8;
- }
- } else {
- print_warning(format!(
- "The 'threads' option in config.toml must be an integer \
- between 1 and {}.\nUsing default.",
- MAX_THREADS
- ))
- }
- }
-
- /// Loads downloads section from the TOML value.
- fn load_downloads_folder_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- self.downloads_folder = s.into();
- } else {
- print_warning(
- "The 'downloads_folder' option in config.toml must be an string.\nUsing \
- default.",
- )
- }
- }
-
- /// Loads dist folder section from the TOML value.
- fn load_dist_folder_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- self.dist_folder = s.into();
- } else {
- print_warning(
- "The 'dist_folder' option in config.toml must be an string.\nUsing \
- default.",
- );
- }
- }
-
- /// Loads results folder section from the TOML value.
- fn load_results_folder_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- self.results_folder = s.into();
- } else {
- print_warning(
- "The 'results_folder' option in config.toml must be an string.\nUsing \
- default.",
- );
- }
- }
-
- /// Loads dex2jar folder section from the TOML value.
- fn load_dex2jar_folder_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- self.dex2jar_folder = s.into();
- } else {
- print_warning(
- "The 'dex2jar_folder' option in config.toml should be an string.\nUsing \
- default.",
- )
- }
- }
-
- /// Loads jd cmd file section from the TOML value.
- fn load_jd_cmd_file_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- let extension = Path::new(&s).extension();
- if extension.is_some() && extension.unwrap() == "jar" {
- self.jd_cmd_file = PathBuf::from(s.clone());
- } else {
- print_warning("The JD-CMD file must be a JAR file.\nUsing default.");
- }
- } else {
- print_warning(
- "The 'jd_cmd_file' option in config.toml must be an string.\nUsing \
- default.",
- )
- }
- }
-
- /// Loads templated folder section from the TOML value.
- fn load_templates_folder_section(&mut self, value: Value) {
- if let Value::String(ref s) = value {
- self.templates_folder = s.into();
- } else {
- print_warning(
- "The 'templates_folder' option in config.toml should be an string.\n\
- Using default.",
- )
- }
- }
-
- /// Loads template section from the TOML value.
- fn load_template_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- self.template = s;
- } else {
- print_warning(
- "The 'template' option in config.toml should be an string.\nUsing \
- default.",
- )
- }
- }
-
- /// Loads rules section from the TOML value.
- fn load_rules_section(&mut self, value: Value) {
- if let Value::String(s) = value {
- let extension = Path::new(&s).extension();
- if extension.is_some() && extension.unwrap() == "json" {
- self.rules_json = PathBuf::from(s.clone());
- } else {
- print_warning(
- "The rules.json file must be a JSON \
- file.\nUsing default.",
- )
- }
- } else {
- print_warning(
- "The 'rules_json' option in config.toml must be an string.\nUsing \
- default.",
- )
- }
- }
-
- /// Loads permissions from the TOML configuration vector.
- fn load_permissions(&mut self, value: Value) {
- if let Value::Array(permissions) = value {
- let format_warning = format!(
- "The permission configuration format must be the following:\n{}\nUsing \
- default.",
- "[[permissions]]\nname=\"unknown|permission.name\"\ncriticality = \
- \"warning|low|medium|high|critical\"\nlabel = \"Permission \
- label\"\ndescription = \"Long description to explain the vulnerability\""
- .italic()
- );
-
- for cfg in permissions {
- let cfg = if let Some(t) = cfg.as_table() {
- t
- } else {
- print_warning(format_warning);
- break;
- };
-
- let name = if let Some(&Value::String(ref n)) = cfg.get("name") {
- n
- } else {
- print_warning(format_warning);
- break;
- };
-
- let criticality = if let Some(&Value::String(ref c)) = cfg.get("criticality") {
- if let Ok(c) = Criticality::from_str(c) {
- c
- } else {
- print_warning(format!(
- "Criticality must be one of {}, {}, {}, {} or \
- {}.\nUsing default.",
- "warning".italic(),
- "low".italic(),
- "medium".italic(),
- "high".italic(),
- "critical".italic()
- ));
- break;
- }
- } else {
- print_warning(format_warning);
- break;
- };
-
- let description = if let Some(&Value::String(ref d)) = cfg.get("description") {
- d.to_owned()
- } else {
- print_warning(format_warning);
- break;
- };
-
- if name == "unknown" {
- if cfg.len() != 3 {
- print_warning(format!(
- "The format for the unknown \
- permissions is the following:\n{}\nUsing default.",
- "[[permissions]]\nname = \
- \"unknown\"\ncriticality = \
- \"warning|low|medium|high|criticality\"\n\
- description = \"Long description to explain \
- the vulnerability\""
- .italic()
- ));
- break;
- }
-
- self.unknown_permission = (criticality, description.clone());
- } else {
- if cfg.len() != 4 {
- print_warning(format_warning);
- break;
- }
-
- let permission = if let Ok(p) = Permission::from_str(name) {
- p
- } else {
- print_warning(format!(
- "Unknown permission: {}\nTo set the default \
- vulnerability level for an unknown permission, \
- please, use the {} permission name, under the {} \
- section.",
- name.italic(),
- "unknown".italic(),
- "[[permissions]]".italic()
- ));
- break;
- };
-
- let label = if let Some(&Value::String(ref l)) = cfg.get("label") {
- l.to_owned()
- } else {
- print_warning(format_warning);
- break;
- };
- self.permissions.insert(PermissionConfig::new(
- permission,
- criticality,
- label,
- description,
- ));
- }
- }
- } else {
- print_warning(
- "You must specify the permissions you want to select as vulnerable.",
- );
- }
- }
-
- /// Loads html report section from the TOML value.
- fn load_html_report_section(&mut self, value: Value) {
- if let Value::Boolean(b) = value {
- self.html = b
- } else {
- print_warning(
- "The 'html_report' option in config.toml should be a boolean.\nUsing \
- default.",
- );
- }
- }
-
- /// Loads json report section from the TOML value.
- fn load_json_report_section(&mut self, value: Value) {
- if let Value::Boolean(b) = value {
- self.json = b;
- } else {
- print_warning(
- "The 'json_report' option in config.toml should be a boolean.\nUsing \
- default.",
- );
- }
- }
-
/// Returns the default `Config` struct.
- fn local_default() -> Config {
- Config {
+ fn local_default() -> Self {
+ Self {
app_packages: Vec::new(),
verbose: false,
quiet: false,
@@ -727,7 +517,7 @@ impl Config {
open: false,
json: false,
html: false,
- threads: 2,
+ threads: num_cpus::get(),
min_criticality: Criticality::Warning,
downloads_folder: PathBuf::from("."),
dist_folder: PathBuf::from("dist"),
@@ -741,8 +531,8 @@ impl Config {
Criticality::Low,
String::from(
"Even if the application can create its own \
- permissions, it's discouraged, since it can \
- lead to missunderstanding between developers.",
+ permissions, it's discouraged, since it can \
+ lead to missunderstanding between developers.",
),
),
permissions: BTreeSet::new(),
@@ -754,13 +544,13 @@ impl Config {
impl Default for Config {
/// Creates the default `Config` struct in Unix systems.
#[cfg(target_family = "unix")]
- fn default() -> Config {
- let mut config = Config::local_default();
+ fn default() -> Self {
+ let mut config = Self::local_default();
let etc_rules = PathBuf::from("/etc/super-analyzer/rules.json");
if etc_rules.exists() {
config.rules_json = etc_rules;
}
- let share_path = Path::new(if cfg!(target_os = "macos") {
+ let share_path = Path::new(if cfg!(taros = "macos") {
"/usr/local/super-analyzer"
} else {
"/usr/share/super-analyzer"
@@ -775,7 +565,7 @@ impl Default for Config {
/// Creates the default `Config` struct in Windows systems.
#[cfg(target_family = "windows")]
- fn default() -> Config {
+ fn default() -> Self {
Config::local_default()
}
}
@@ -784,10 +574,10 @@ impl Default for Config {
///
/// Represents a Permission with all its fields. Implements the `PartialEq` and `PartialOrd`
/// traits.
-#[derive(Debug, Ord, Eq)]
-pub struct PermissionConfig {
+#[derive(Debug, Ord, Eq, Deserialize)]
+pub struct Permission {
/// Permission name.
- permission: Permission,
+ name: manifest::Permission,
/// Permission criticality.
criticality: Criticality,
/// Permission label.
@@ -796,93 +586,75 @@ pub struct PermissionConfig {
description: String,
}
-impl PartialEq for PermissionConfig {
- fn eq(&self, other: &PermissionConfig) -> bool {
- self.permission == other.permission
+impl PartialEq for Permission {
+ fn eq(&self, other: &Self) -> bool {
+ self.name == other.name
}
}
-impl PartialOrd for PermissionConfig {
- fn partial_cmp(&self, other: &PermissionConfig) -> Option {
- if self.permission < other.permission {
- Some(Ordering::Less)
- } else if self.permission > other.permission {
- Some(Ordering::Greater)
- } else {
- Some(Ordering::Equal)
- }
+impl PartialOrd for Permission {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ Some(self.name.cmp(&other.name))
}
}
-impl PermissionConfig {
- /// Creates a new `PermissionConfig`.
- fn new, D: Into>(
- permission: Permission,
- criticality: Criticality,
- label: L,
- description: D,
- ) -> PermissionConfig {
- PermissionConfig {
- permission: permission,
- criticality: criticality,
- label: label.into(),
- description: description.into(),
- }
- }
-
- /// Returns the enum that represents the `permission`.
- pub fn get_permission(&self) -> Permission {
- self.permission
+impl Permission {
+ /// Returns the enum that represents the `name`.
+ pub fn name(&self) -> manifest::Permission {
+ self.name
}
/// Returns the permission's `criticality`.
- pub fn get_criticality(&self) -> Criticality {
+ pub fn criticality(&self) -> Criticality {
self.criticality
}
/// Returns the permission's `label`.
- pub fn get_label(&self) -> &str {
+ pub fn label(&self) -> &str {
self.label.as_str()
}
/// Returns the permission's `description`.
- pub fn get_description(&self) -> &str {
+ pub fn description(&self) -> &str {
self.description.as_str()
}
}
+/// Test module for the configuration.
#[cfg(test)]
mod tests {
- use Criticality;
- use static_analysis::manifest::Permission;
+ use std::{
+ fs,
+ path::{Path, PathBuf},
+ };
+
+ use num_cpus;
+
use super::Config;
- use std::fs;
- use std::path::{Path, PathBuf};
- use std::collections::BTreeMap;
- use std::str::FromStr;
- use toml::*;
+ use crate::{criticality::Criticality, static_analysis::manifest};
/// Test for the default configuration function.
#[test]
+ #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
fn it_config() {
// Create config object.
let mut config = Config::default();
// Check that the properties of the config object are correct.
- assert!(config.get_app_packages().is_empty());
+ assert!(config.app_packages().is_empty());
assert!(!config.is_verbose());
assert!(!config.is_quiet());
assert!(!config.is_force());
assert!(!config.is_bench());
assert!(!config.is_open());
- assert_eq!(config.get_threads(), 2);
+ assert_eq!(config.threads(), num_cpus::get());
assert_eq!(config.downloads_folder, Path::new("."));
- assert_eq!(config.get_dist_folder(), Path::new("dist"));
- assert_eq!(config.get_results_folder(), Path::new("results"));
- assert_eq!(config.get_template_name(), "super");
- let share_path = Path::new(if cfg!(target_os = "macos") {
+ assert_eq!(config.dist_folder(), Path::new("dist"));
+ assert_eq!(config.results_folder(), Path::new("results"));
+ assert_eq!(config.template_name(), "super");
+ let share_path = Path::new(if cfg!(taros = "macos") {
"/usr/local/super-analyzer"
- } else if cfg!(target_family = "windows") {
+ } else if cfg!(tarfamily = "windows") {
""
} else {
"/usr/share/super-analyzer"
@@ -893,45 +665,42 @@ mod tests {
Path::new("")
};
assert_eq!(
- config.get_dex2jar_folder(),
+ config.dex2jar_folder(),
share_path.join("vendor").join("dex2jar-2.1-SNAPSHOT")
);
assert_eq!(
- config.get_jd_cmd_file(),
+ config.jd_cmd_file(),
share_path.join("vendor").join("jd-cmd.jar")
);
- assert_eq!(config.get_templates_folder(), share_path.join("templates"));
+ assert_eq!(config.templates_folder(), share_path.join("templates"));
assert_eq!(
- config.get_template_path(),
+ config.template_path(),
share_path.join("templates").join("super")
);
- if cfg!(target_family = "unix") && Path::new("/etc/super-analyzer/rules.json").exists() {
+ if cfg!(tarfamily = "unix") && Path::new("/etc/super-analyzer/rules.json").exists() {
assert_eq!(
- config.get_rules_json(),
+ config.rules_json(),
Path::new("/etc/super-analyzer/rules.json")
);
} else {
- assert_eq!(config.get_rules_json(), Path::new("rules.json"));
+ assert_eq!(config.rules_json(), Path::new("rules.json"));
}
+ assert_eq!(config.unknown_permission_criticality(), Criticality::Low);
assert_eq!(
- config.get_unknown_permission_criticality(),
- Criticality::Low
- );
- assert_eq!(
- config.get_unknown_permission_description(),
+ config.unknown_permission_description(),
"Even if the application can create its own permissions, it's discouraged, \
- since it can lead to missunderstanding between developers."
+ since it can lead to missunderstanding between developers."
);
- assert_eq!(config.get_permissions().next(), None);
+ assert_eq!(config.permissions().next(), None);
if !config.downloads_folder.exists() {
fs::create_dir(&config.downloads_folder).unwrap();
}
- if !config.get_dist_folder().exists() {
- fs::create_dir(config.get_dist_folder()).unwrap();
+ if !config.dist_folder().exists() {
+ fs::create_dir(config.dist_folder()).unwrap();
}
- if !config.get_results_folder().exists() {
- fs::create_dir(config.get_results_folder()).unwrap();
+ if !config.results_folder().exists() {
+ fs::create_dir(config.results_folder()).unwrap();
}
// Change properties.
@@ -943,7 +712,7 @@ mod tests {
config.open = true;
// Check that the new properties are correct.
- let packages = config.get_app_packages();
+ let packages = config.app_packages();
assert_eq!(&packages[0], &config.downloads_folder.join("test_app.apk"));
assert!(config.is_verbose());
assert!(config.is_quiet());
@@ -976,410 +745,56 @@ mod tests {
#[test]
fn it_config_sample() {
// Create config object.
- let mut config = Config::default();
- config.load_from_file("config.toml.sample").unwrap();
+ let mut config = Config::from_file(&PathBuf::from("config.toml.sample")).unwrap();
config.add_app_package("test_app");
// Check that the properties of the config object are correct.
- assert_eq!(config.get_threads(), 2);
+ assert_eq!(config.threads(), 2);
assert_eq!(config.downloads_folder, Path::new("downloads"));
- assert_eq!(config.get_dist_folder(), Path::new("dist"));
- assert_eq!(config.get_results_folder(), Path::new("results"));
+ assert_eq!(config.dist_folder(), Path::new("dist"));
+ assert_eq!(config.results_folder(), Path::new("results"));
assert_eq!(
- config.get_dex2jar_folder(),
+ config.dex2jar_folder(),
Path::new("/usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT")
);
assert_eq!(
- config.get_jd_cmd_file(),
+ config.jd_cmd_file(),
Path::new("/usr/share/super-analyzer/vendor/jd-cmd.jar")
);
assert_eq!(
- config.get_templates_folder(),
+ config.templates_folder(),
Path::new("/usr/share/super-analyzer/templates")
);
assert_eq!(
- config.get_template_path(),
+ config.template_path(),
Path::new("/usr/share/super-analyzer/templates/super")
);
- assert_eq!(config.get_template_name(), "super");
+ assert_eq!(config.template_name(), "super");
assert_eq!(
- config.get_rules_json(),
+ config.rules_json(),
Path::new("/etc/super-analyzer/rules.json")
);
+ assert_eq!(config.unknown_permission_criticality(), Criticality::Low);
assert_eq!(
- config.get_unknown_permission_criticality(),
- Criticality::Low
- );
- assert_eq!(
- config.get_unknown_permission_description(),
+ config.unknown_permission_description(),
"Even if the application can create its own permissions, it's discouraged, \
- since it can lead to missunderstanding between developers."
+ since it can lead to missunderstanding between developers."
);
- let permission = config.get_permissions().next().unwrap();
+ let permission = config.permissions().next().unwrap();
assert_eq!(
- permission.get_permission(),
- Permission::AndroidPermissionInternet
+ permission.name(),
+ manifest::Permission::AndroidPermissionInternet
);
- assert_eq!(permission.get_criticality(), Criticality::Warning);
- assert_eq!(permission.get_label(), "Internet permission");
+ assert_eq!(permission.criticality(), Criticality::Warning);
+ assert_eq!(permission.label(), "Internet permission");
assert_eq!(
- permission.get_description(),
+ permission.description(),
"Allows the app to create network sockets and use custom network protocols. \
- The browser and other applications provide means to send data to the \
- internet, so this permission is not required to send data to the internet. \
- Check if the permission is actually needed."
- );
- }
-
- /// Test to check a valid jd cmd file section is loaded
- #[test]
- fn it_loads_jd_cmd_file_section_if_it_is_well_formed() {
- let mut final_config = Config::default();
- let value = Value::String("/some/path/to/jd-cmd.jar".to_string());
-
- final_config.load_jd_cmd_file_section(value);
-
- assert_eq!(
- PathBuf::from("/some/path/to/jd-cmd.jar"),
- final_config.jd_cmd_file
- )
- }
-
- /// Test to check an invalid jd cmd file section is not loaded
- #[test]
- fn it_do_not_load_jd_cmd_file_section_if_it_is_not_well_formed() {
- let default_config = Config::default();
- let mut final_config = Config::default();
-
- let values = vec![
- Value::String("/some/invalid/js_cmd.jpg".to_string()),
- Value::Integer(20),
- ];
-
- for value in values {
- final_config.load_jd_cmd_file_section(value);
- assert_eq!(default_config.jd_cmd_file, final_config.jd_cmd_file)
- }
- }
-
- /// Test to check a valid threads section is loaded
- #[test]
- fn it_loads_threads_section_if_it_is_well_formed() {
- let default_config = Config::default();
- let mut final_config = Config::default();
- let value = Value::Integer((default_config.threads + 1) as i64);
-
- final_config.load_threads_section(value);
-
- assert_eq!(default_config.threads + 1, final_config.threads)
- }
-
- /// Test to check an invalid threads section is not loaded
- #[test]
- fn it_do_not_loads_threads_section_if_it_is_not_well_formed() {
- let default_config = Config::default();
- let mut final_config = Config::default();
-
- let values = vec![Value::Integer(super::MAX_THREADS + 1), Value::Float(2.4)];
-
- for value in values {
- final_config.load_threads_section(value);
- assert_eq!(default_config.threads, final_config.threads)
- }
- }
-
- /// Test to check mixed sections that should receive string data
- #[test]
- fn it_loads_string_data_on_some_sections() {
- let mut final_config = Config::default();
- let str_value = "Valid string".to_string();
-
- final_config.load_downloads_folder_section(Value::String(str_value.clone()));
- final_config.load_dist_folder_section(Value::String(str_value.clone()));
- final_config.load_results_folder_section(Value::String(str_value.clone()));
- final_config.load_dex2jar_folder_section(Value::String(str_value.clone()));
- final_config.load_templates_folder_section(Value::String(str_value.clone()));
- final_config.load_template_section(Value::String(str_value.clone()));
-
- assert_eq!(
- final_config.downloads_folder,
- PathBuf::from(str_value.clone())
- );
- assert_eq!(final_config.dist_folder, PathBuf::from(str_value.clone()));
- assert_eq!(
- final_config.results_folder,
- PathBuf::from(str_value.clone())
- );
- assert_eq!(
- final_config.dex2jar_folder,
- PathBuf::from(str_value.clone())
- );
- assert_eq!(
- final_config.templates_folder,
- PathBuf::from(str_value.clone())
- );
- assert_eq!(final_config.template, str_value.clone());
- }
-
- /// Test to check mixed sections that should receive boolean data
- #[test]
- fn it_loads_boolean_data_on_some_sections() {
- let mut final_config = Config::default();
-
- final_config.load_html_report_section(Value::Boolean(false));
- final_config.load_json_report_section(Value::Boolean(true));
-
- assert_eq!(final_config.html, false);
- assert_eq!(final_config.json, true);
- }
-
- /// Test to check a valid rules section is loaded
- #[test]
- fn it_loads_rules_section_if_it_is_well_formed() {
- let mut final_config = Config::default();
- let value = Value::String("/some/path/to/rules.json".to_string());
-
- final_config.load_rules_section(value);
-
- assert_eq!(
- PathBuf::from("/some/path/to/rules.json"),
- final_config.rules_json
- )
- }
-
- /// Test to check an invalid rules section is not loaded
- #[test]
- fn it_do_not_load_rules_section_if_it_is_not_well_formed() {
- let default_config = Config::default();
- let mut final_config = Config::default();
-
- let values = vec![
- Value::String("/some/invalid/rules.jpg".to_string()),
- Value::Integer(20),
- ];
-
- for value in values {
- final_config.load_rules_section(value);
- assert_eq!(default_config.rules_json, final_config.rules_json)
- }
- }
-
- #[test]
- fn it_do_not_load_permissions_if_they_are_not_well_formed() {
- let default_config = Config::default();
- let mut final_config = Config::default();
-
- let permission_without_name: BTreeMap = BTreeMap::new();
-
- let mut permission_invalid_criticality: BTreeMap = BTreeMap::new();
- permission_invalid_criticality
- .insert(
- "name".to_string(),
- Value::String("permission_name".to_string()),
- )
- .is_some();
- permission_invalid_criticality
- .insert(
- "criticality".to_string(),
- Value::String("invalid_level".to_string()),
- )
- .is_some();
-
- let mut permission_without_criticality: BTreeMap = BTreeMap::new();
- permission_without_criticality
- .insert(
- "name".to_string(),
- Value::String("permission_name".to_string()),
- )
- .is_some();
-
- let mut permission_without_description: BTreeMap = BTreeMap::new();
- permission_without_description
- .insert(
- "name".to_string(),
- Value::String("permission_name".to_string()),
- )
- .is_some();
- permission_without_description
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- permission_without_description
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
-
- let mut permission_unknown_too_much_values: BTreeMap = BTreeMap::new();
- permission_unknown_too_much_values
- .insert("name".to_string(), Value::String("unknown".to_string()))
- .is_some();
- permission_unknown_too_much_values
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- permission_unknown_too_much_values
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
- permission_unknown_too_much_values
- .insert(
- "additional_field".to_string(),
- Value::String("additional field data".to_string()),
- )
- .is_some();
-
- let mut permission_known_too_much_values: BTreeMap = BTreeMap::new();
- permission_known_too_much_values
- .insert(
- "name".to_string(),
- Value::String("android.permission.ACCESS_ALL_EXTERNAL_STORAGE".to_string()),
- )
- .is_some();
- permission_known_too_much_values
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- permission_known_too_much_values
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
- permission_known_too_much_values
- .insert("label".to_string(), Value::String("label".to_string()))
- .is_some();
- permission_known_too_much_values
- .insert(
- "additional_field".to_string(),
- Value::String("additional field data".to_string()),
- )
- .is_some();
-
- let mut permission_known_name_not_found: BTreeMap = BTreeMap::new();
- permission_known_name_not_found
- .insert(
- "name".to_string(),
- Value::String("invalid name".to_string()),
- )
- .is_some();
- permission_known_name_not_found
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- permission_known_name_not_found
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
- permission_known_name_not_found
- .insert("label".to_string(), Value::String("label".to_string()))
- .is_some();
-
- let mut permission_without_label: BTreeMap = BTreeMap::new();
- permission_without_label
- .insert(
- "name".to_string(),
- Value::String("invalid name".to_string()),
- )
- .is_some();
- permission_without_label
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- permission_without_label
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
- permission_without_label
- .insert(
- "additional_field".to_string(),
- Value::String("additional field data".to_string()),
- )
- .is_some();
-
- let permissions = vec![
- Value::Integer(20),
- Value::Table(permission_without_name),
- Value::Table(permission_invalid_criticality),
- Value::Table(permission_without_criticality),
- Value::Table(permission_without_description),
- Value::Table(permission_unknown_too_much_values),
- Value::Table(permission_known_too_much_values),
- Value::Table(permission_known_name_not_found),
- Value::Table(permission_without_label),
- ];
-
- for p in permissions {
- final_config.load_permissions(Value::Array(vec![p]));
-
- assert_eq!(default_config.permissions, final_config.permissions);
- assert_eq!(
- default_config.unknown_permission,
- final_config.unknown_permission
- );
- }
- }
-
-
- #[test]
- fn it_loads_an_unknown_permission() {
- let mut final_config = Config::default();
-
- let mut unknown_permission: BTreeMap = BTreeMap::new();
- unknown_permission
- .insert("name".to_string(), Value::String("unknown".to_string()))
- .is_some();
- unknown_permission
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- unknown_permission
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
-
- final_config.load_permissions(Value::Array(vec![Value::Table(unknown_permission)]));
- assert_eq!(
- final_config.get_unknown_permission_criticality(),
- Criticality::from_str("low").unwrap()
+ The browser and other applications provide means to send data to the \
+ internet, so this permission is not required to send data to the internet. \
+ Check if the permission is actually needed."
);
- assert_eq!(
- final_config.get_unknown_permission_description(),
- "permission description"
- );
- }
-
- #[test]
- fn it_loads_an_known_permission() {
- let mut final_config = Config::default();
-
- let mut unknown_permission: BTreeMap = BTreeMap::new();
- unknown_permission
- .insert(
- "name".to_string(),
- Value::String("android.permission.ACCESS_ALL_EXTERNAL_STORAGE".to_string()),
- )
- .is_some();
- unknown_permission
- .insert("criticality".to_string(), Value::String("low".to_string()))
- .is_some();
- unknown_permission
- .insert(
- "description".to_string(),
- Value::String("permission description".to_string()),
- )
- .is_some();
- unknown_permission
- .insert("label".to_string(), Value::String("label".to_string()))
- .is_some();
-
- final_config.load_permissions(Value::Array(vec![Value::Table(unknown_permission)]));
-
- assert_eq!(final_config.get_permissions().len(), 1)
}
/// Test to check the default reports to be generated
diff --git a/src/criticality.rs b/src/criticality.rs
new file mode 100644
index 000000000..0f1940571
--- /dev/null
+++ b/src/criticality.rs
@@ -0,0 +1,72 @@
+//! Criticality module.
+
+use std::{
+ fmt::{self, Display},
+ str::FromStr,
+};
+
+use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+
+use crate::error;
+
+/// Vulnerability criticality
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
+pub enum Criticality {
+ /// Warning.
+ Warning,
+ /// Low criticality vulnerability.
+ Low,
+ /// Medium criticality vulnerability.
+ Medium,
+ /// High criticality vulnerability.
+ High,
+ /// Critical vulnerability.
+ Critical,
+}
+
+impl Display for Criticality {
+ #[cfg_attr(feature = "cargo-clippy", allow(use_debug))]
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ write!(f, "{}", format!("{:?}", self).to_lowercase())
+ }
+}
+
+impl Serialize for Criticality {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(format!("{}", self).as_str())
+ }
+}
+
+impl<'de> Deserialize<'de> for Criticality {
+ fn deserialize(de: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ let criticality_str: String = Deserialize::deserialize(de)?;
+
+ match Self::from_str(criticality_str.as_str()) {
+ Ok(criticality) => Ok(criticality),
+ Err(_) => Err(de::Error::custom(format!(
+ "unknown criticality: `{}`",
+ criticality_str
+ ))),
+ }
+ }
+}
+
+impl FromStr for Criticality {
+ type Err = error::Kind;
+ fn from_str(s: &str) -> Result {
+ match s.to_lowercase().as_str() {
+ "critical" => Ok(Criticality::Critical),
+ "high" => Ok(Criticality::High),
+ "medium" => Ok(Criticality::Medium),
+ "low" => Ok(Criticality::Low),
+ "warning" => Ok(Criticality::Warning),
+ _ => Err(error::Kind::Parse),
+ }
+ }
+}
diff --git a/src/decompilation.rs b/src/decompilation.rs
index 32e1bb5b7..082268c56 100644
--- a/src/decompilation.rs
+++ b/src/decompilation.rs
@@ -1,26 +1,20 @@
//! Decompilation module.
//!
-//! Handles the extraction, decompression and decompilation of _.apks_
+//! Handles the extraction, decompression and decompilation of `_.apks_`
-use std::fs;
-use std::path::Path;
-use std::process::Command;
-use std::error::Error as StdError;
+use std::{fs, path::Path, process::Command};
-use colored::Colorize;
use abxml::apk::Apk;
+use colored::Colorize;
+use failure::{bail, format_err, Error, ResultExt};
-use error::*;
-use {Config, print_warning, get_package_name};
-
-/// Decompresses the application using _Apktool_.
-pub fn decompress>(config: &mut Config, package: P) -> Result<()> {
- let path = config.get_dist_folder().join(
- package
- .as_ref()
- .file_stem()
- .unwrap(),
- );
+use crate::{get_package_name, print_warning, Config};
+
+/// Decompresses the application using `_Apktool_`.
+pub fn decompress>(config: &mut Config, package: P) -> Result<(), Error> {
+ let path = config
+ .dist_folder()
+ .join(package.as_ref().file_stem().unwrap());
if !path.exists() || config.is_force() {
if path.exists() {
if config.is_verbose() {
@@ -29,9 +23,8 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()>
if let Err(e) = fs::remove_dir_all(&path) {
print_warning(format!(
- "There was an error when removing the decompression \
- folder: {}",
- e.description()
+ "there was an error when removing the decompression folder: {}",
+ e
));
}
}
@@ -42,12 +35,11 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()>
println!("Decompressing the application…");
}
- let mut apk = Apk::new(package.as_ref()).chain_err(
- || "error loading apk file",
- )?;
- apk.export(&path, true).chain_err(
- || "could not decompress the apk file",
- )?;
+ let mut apk = Apk::new(package.as_ref()).context("error loading apk file")?;
+ apk.export(&path, true).context(format_err!(
+ "could not decompress the apk file. Tried to decompile at: {}",
+ path.display()
+ ))?;
if config.is_verbose() {
println!(
@@ -62,8 +54,8 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()>
}
} else if config.is_verbose() {
println!(
- "Seems that the application has already been decompressed. There is no need to \
- do it again."
+ "Seems that the application has already been decompressed. There is no need to do it \
+ again."
);
} else {
println!("Skipping decompression.");
@@ -72,56 +64,47 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()>
Ok(())
}
-/// Converts _.dex_ files to _.jar_ using _Dex2jar_.
-pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<()> {
+/// Converts `_.dex_` files to `_.jar_` using `_Dex2jar_`.
+pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<(), Error> {
let package_name = get_package_name(package.as_ref());
- let classes = config.get_dist_folder().join(&package_name).join(
- "classes.jar",
- );
+ let classes = config.dist_folder().join(&package_name).join("classes.jar");
if config.is_force() || !classes.exists() {
config.set_force();
// Command to convert .dex to .jar. using dex2jar.
// "-o path" to specify an output file
- let output = Command::new(config.get_dex2jar_folder().join(if cfg!(
- target_family = "windows"
- )
- {
- "d2j-dex2jar.bat"
- } else {
- "d2j-dex2jar.sh"
- })).arg(config.get_dist_folder().join(&package_name).join(
- "classes.dex",
- ))
- .arg("-f")
- .arg("-o")
- .arg(&classes)
- .output()
- .chain_err(|| {
- format!(
- "There was an error when executing the {} to {} conversion command",
- ".dex".italic(),
- ".jar".italic()
- )
- })?;
+ let output = Command::new(config.dex2jar_folder().join(
+ if cfg!(target_family = "windows") {
+ "d2j-dex2jar.bat"
+ } else {
+ "d2j-dex2jar.sh"
+ },
+ )).arg(config.dist_folder().join(&package_name).join("classes.dex"))
+ .arg("-f")
+ .arg("-o")
+ .arg(&classes)
+ .output()
+ .context(format_err!(
+ "there was an error when executing the {} to {} conversion command",
+ ".dex".italic(),
+ ".jar".italic()
+ ))?;
let stderr = String::from_utf8_lossy(&output.stderr);
// Here a small hack: seems that dex2jar outputs in stderr even if everything went well,
// and the status is always success. So the only difference is if we detect the actual
// exception that was produced. But, the thing is that in some cases it does not return an
// exception, so we have to check if errors such as "use certain option" occur.
- if !output.status.success() || stderr.find('\n') != Some(stderr.len() - 1) ||
- stderr.contains("use")
+ if !output.status.success()
+ || stderr.find('\n') != Some(stderr.len() - 1)
+ || stderr.contains("use")
{
- let message = format!(
- "The {} to {} conversion command returned an error. More info: \
- {}",
+ bail!(
+ "the {} to {} conversion command returned an error. More info: {}",
".dex".italic(),
".jar".italic(),
stderr
);
-
- return Err(message.into());
}
if config.is_verbose() {
@@ -140,7 +123,7 @@ pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<()>
} else if config.is_verbose() {
println!(
"Seems that there is already a {} file for the application. There is no need to \
- create it again.",
+ create it again.",
".jar".italic()
);
} else {
@@ -150,32 +133,29 @@ pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<()>
Ok(())
}
-/// Decompiles the application using _jd\_cmd_.
-pub fn decompile>(config: &mut Config, package: P) -> Result<()> {
+/// Decompiles the application using `_jd\_cmd_`.
+pub fn decompile>(config: &mut Config, package: P) -> Result<(), Error> {
let package_name = get_package_name(package.as_ref());
- let out_path = config.get_dist_folder().join(&package_name).join("classes");
+ let out_path = config.dist_folder().join(&package_name).join("classes");
if config.is_force() || !out_path.exists() {
config.set_force();
- // Command to decompile the application using jd_cmd.
+ // Command to decompile the application using `jd_cmd`.
// "-od path" to specify an output directory
let output = Command::new("java")
.arg("-jar")
- .arg(config.get_jd_cmd_file())
- .arg(config.get_dist_folder().join(&package_name).join(
- "classes.jar",
- ))
+ .arg(config.jd_cmd_file())
+ .arg(config.dist_folder().join(&package_name).join("classes.jar"))
.arg("-od")
.arg(&out_path)
.output()
- .chain_err(|| "There was an unknown error decompiling the application")?;
+ .context("there was an unknown error decompiling the application")?;
if !output.status.success() {
- let message = format!(
- "The decompilation command returned an error. More info:\n{}",
+ bail!(
+ "the decompilation command returned an error. More info:\n{}",
String::from_utf8_lossy(&output.stdout)
);
- return Err(message.into());
}
if config.is_verbose() {
@@ -188,8 +168,8 @@ pub fn decompile>(config: &mut Config, package: P) -> Result<()>
}
} else if config.is_verbose() {
println!(
- "Seems that there is already a source folder for the application. There is no \
- need to decompile it again."
+ "Seems that there is already a source folder for the application. There is no need to \
+ decompile it again."
);
} else {
println!("Skipping decompilation.");
diff --git a/src/error.rs b/src/error.rs
index 231518045..2ee47dd61 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,52 +1,27 @@
-//! Module containing the definition of error chain types.
-#![allow(large_enum_variant)]
+//! Module containing the definition of error types.
-error_chain! {
- foreign_links {
- IO(::std::io::Error);
- Template(::handlebars::TemplateFileError);
- TemplateRender(::handlebars::RenderError);
- JSON(::serde_json::error::Error);
- TOML(::toml::de::Error);
- }
-
- errors {
- /// Configuration error.
- Config(message: String) {
- description("there was an error in the configuration")
- display("there was an error in the configuration: {}", message)
- }
- /// Parsing error.
- Parse {
- description("there was an error in some parsing process")
- }
- /// Template name error.
- TemplateName(message: String) {
- description("Invalid template name")
- display("{}", message)
- }
- /// Code not found.
- CodeNotFound {
- description("the code was not found in the file")
- }
- }
-}
-
-impl Into for Error {
- fn into(self) -> i32 {
- let kind = self.kind();
-
- match *kind {
- ErrorKind::Parse |
- ErrorKind::TOML(_) => 20,
- ErrorKind::JSON(_) => 30,
- ErrorKind::CodeNotFound => 40,
- ErrorKind::Config(_) => 50,
- ErrorKind::IO(_) => 100,
- ErrorKind::TemplateName(_) => 125,
- ErrorKind::Template(_) => 150,
- ErrorKind::TemplateRender(_) => 175,
- ErrorKind::Msg(_) => 1,
- }
- }
+/// Enumeration of the different error kinds.
+#[derive(Debug, Fail)]
+pub enum Kind {
+ /// Configuration error.
+ #[fail(
+ display = "there was an error in the configuration: {}",
+ message
+ )]
+ Config {
+ /// Error message.
+ message: String,
+ },
+ /// Parsing error.
+ #[fail(display = "there was an error in the parsing process")]
+ Parse,
+ /// Template name error.
+ #[fail(display = "invalid template name: {}", message)]
+ TemplateName {
+ /// Error message.
+ message: String,
+ },
+ /// Code not found.
+ #[fail(display = "no code was found in the file")]
+ CodeNotFound,
}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 000000000..308868490
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,503 @@
+//! SUPER Android Analyzer core library.
+//!
+//! This library contains the code for analyzing Android applications. It's called by the
+//! launcher and contains the main logic of the analysis, with the configuration management,
+//! the logger initialization and some utility functions.
+
+#![cfg_attr(feature = "cargo-clippy", deny(clippy))]
+#![forbid(anonymous_parameters)]
+#![cfg_attr(feature = "cargo-clippy", warn(clippy_pedantic))]
+#![deny(
+ variant_size_differences,
+ unused_results,
+ unused_qualifications,
+ unused_import_braces,
+ unsafe_code,
+ trivial_numeric_casts,
+ trivial_casts,
+ missing_docs,
+ unused_extern_crates,
+ missing_debug_implementations,
+ missing_copy_implementations,
+)]
+// Allowing these for now.
+#![cfg_attr(
+ feature = "cargo-clippy",
+ allow(
+ stutter,
+ cast_possible_truncation,
+ cast_possible_wrap,
+ cast_precision_loss,
+ cast_sign_loss,
+ non_ascii_literal,
+ )
+)]
+
+extern crate abxml;
+extern crate bytecount;
+extern crate chrono;
+extern crate clap;
+extern crate colored;
+extern crate env_logger;
+extern crate failure;
+#[macro_use]
+extern crate failure_derive;
+extern crate handlebars;
+extern crate hex;
+extern crate lazy_static;
+#[macro_use]
+extern crate log;
+extern crate md5;
+extern crate num_cpus;
+extern crate open;
+extern crate regex;
+extern crate semver;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_json;
+extern crate sha1;
+extern crate sha2;
+extern crate toml;
+extern crate xml;
+
+pub mod cli;
+mod config;
+mod criticality;
+mod decompilation;
+pub mod error;
+mod results;
+mod static_analysis;
+mod utils;
+
+use std::{
+ collections::BTreeMap,
+ env, fs,
+ path::Path,
+ thread::sleep,
+ time::{Duration, Instant},
+};
+
+use clap::ArgMatches;
+use colored::Colorize;
+use failure::{bail, format_err, Error, ResultExt};
+
+pub use crate::{
+ config::Config,
+ utils::{
+ get_code, get_package_name, get_string, print_vulnerability, print_warning, Benchmark,
+ PARSER_CONFIG,
+ },
+};
+use crate::{
+ decompilation::{decompile, decompress, dex_to_jar},
+ results::Results,
+ static_analysis::static_analysis,
+};
+
+/// Logo ASCII art, used in verbose mode.
+pub static BANNER: &str = include_str!("banner.txt");
+
+/// Initialize the config with the config files and command line options.
+///
+/// On UNIX, if local file, `config.toml`, does not exist, but the global one does
+/// `/etc/super-analyzer/config.toml`, the latter is used. Otherwise, the local file
+/// is used. Finally, if non of the files could be loaded, the default configuration
+/// is used. This default configuration contains the minimal setup for running the
+/// analysis.
+///
+/// It will then add the configuration selected with the command line interface options.
+pub fn initialize_config(cli: &ArgMatches<'static>) -> Result {
+ let config_path = Path::new("config.toml");
+ let global_config_path = Path::new("/etc/super-analyzer/config.toml");
+
+ let mut config =
+ if cfg!(target_family = "unix") && !config_path.exists() && global_config_path.exists() {
+ Config::from_file(&global_config_path).context(
+ "there was an error when reading the /etc/super-analyzer/config.toml file",
+ )?
+ } else if config_path.exists() {
+ Config::from_file(&config_path)
+ .context("there was an error when reading the config.toml file")?
+ } else {
+ print_warning("config file not found. Using default configuration");
+ Config::default()
+ };
+
+ config
+ .decorate_with_cli(cli)
+ .context("there was an error reading the configuration from the CLI")?;
+
+ Ok(config)
+}
+
+/// Analyzes the given package with the given configuration.
+#[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+pub fn analyze_package>(
+ package: P,
+ config: &mut Config,
+ benchmarks: &mut BTreeMap>,
+) -> Result<(), Error> {
+ let package_name = get_package_name(&package);
+ if config.is_bench() {
+ let _ = benchmarks.insert(package_name.clone(), Vec::with_capacity(4));
+ }
+ if !config.is_quiet() {
+ println!();
+ println!("Starting analysis of {}.", package_name.italic());
+ }
+
+ // Apk decompression.
+ let start_time = Instant::now();
+ decompress(config, &package).context("apk decompression failed")?;
+
+ if config.is_bench() {
+ benchmarks
+ .get_mut(&package_name)
+ .unwrap()
+ .push(Benchmark::new("Apk decompression", start_time.elapsed()));
+ }
+
+ // Converting the .dex to .jar.
+ let dex_jar_time = Instant::now();
+ dex_to_jar(config, &package).context("conversion from DEX to JAR failed")?;
+
+ if config.is_bench() {
+ benchmarks
+ .get_mut(&package_name)
+ .unwrap()
+ .push(Benchmark::new(
+ "Dex to Jar decompilation (dex2jar Java dependency)",
+ dex_jar_time.elapsed(),
+ ));
+ }
+
+ if config.is_verbose() {
+ println!();
+ println!(
+ "Now it's time for the actual decompilation of the source code. We'll translate
+ Android JVM bytecode to Java, so that we can check the code afterwards."
+ );
+ }
+
+ // Decompiling the app
+ let decompile_start = Instant::now();
+ decompile(config, &package).context("JAR decompression failed")?;
+
+ if config.is_bench() {
+ benchmarks
+ .get_mut(&package_name)
+ .unwrap()
+ .push(Benchmark::new(
+ "Decompilation (jd-cli Java dependency)",
+ decompile_start.elapsed(),
+ ));
+ }
+
+ // Initialize results structure
+ let mut results = Results::init(config, &package)?;
+
+ // Static application analysis
+ let static_start = Instant::now();
+ static_analysis(config, &package_name, &mut results);
+
+ if config.is_bench() {
+ benchmarks
+ .get_mut(&package_name)
+ .unwrap()
+ .push(Benchmark::new(
+ "Total static analysis",
+ static_start.elapsed(),
+ ));
+ }
+
+ if !config.is_quiet() {
+ println!();
+ }
+
+ // Generate results report.
+ let report_start = Instant::now();
+ results
+ .generate_report(config, &package_name)
+ .context(format_err!(
+ "there was an error generating the results report at: {}",
+ config.results_folder().join(&package_name).display()
+ ))?;
+
+ if config.is_verbose() {
+ println!("Everything went smoothly, you can now check all the results.");
+ println!();
+ println!("I will now analyze myself for vulnerabilities…");
+ sleep(Duration::from_millis(1500));
+ println!(
+ "Nah, just kidding, I've been developed in {}!",
+ "Rust".bold().green()
+ )
+ }
+
+ if config.is_bench() {
+ benchmarks
+ .get_mut(&package_name)
+ .unwrap()
+ .push(Benchmark::new("Report generation", report_start.elapsed()));
+ benchmarks
+ .get_mut(&package_name)
+ .unwrap()
+ .push(Benchmark::new(
+ format!("Total time for {}", package_name),
+ start_time.elapsed(),
+ ));
+ }
+
+ if config.is_open() {
+ let open_path = if config.has_to_generate_html() {
+ config
+ .results_folder()
+ .join(results.app_package())
+ .join("index.html")
+ } else {
+ config
+ .results_folder()
+ .join(results.app_package())
+ .join("results.json")
+ };
+
+ let status =
+ open::that(open_path).context("the report could not be opened automatically")?;
+
+ if !status.success() {
+ bail!("report opening errored with status code: {}", status);
+ }
+ }
+
+ Ok(())
+}
+
+/// Copies the contents of `from` to `to`
+///
+/// If the destination folder doesn't exist is created. Note that the parent folder must exist. If
+/// files in the destination folder exist with the same name as in the origin folder, they will be
+/// overwriten.
+pub fn copy_folder>(from: P, to: P) -> Result<(), Error> {
+ if !to.as_ref().exists() {
+ fs::create_dir(to.as_ref())?;
+ }
+
+ for f in fs::read_dir(from)? {
+ let f = f?;
+ if f.path().is_dir() {
+ copy_folder(
+ f.path(),
+ to.as_ref()
+ .join(f.path().file_name().expect("expected file name")),
+ )?;
+ } else {
+ let _ = fs::copy(
+ f.path(),
+ to.as_ref()
+ .join(f.path().file_name().expect("expected file name")),
+ )?;
+ }
+ }
+ Ok(())
+}
+
+/// Initializes the logger.
+///
+/// This will initialize the environment logger structure so that it generates the
+/// proper messages using the right colors. It's called from the launcher.
+#[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+pub fn initialize_logger(is_verbose: bool) -> Result<(), log::SetLoggerError> {
+ use env_logger::fmt::{Color, Formatter};
+ use env_logger::Builder;
+ use log::{Level, LevelFilter, Record};
+ use std::io::Write;
+
+ // Define the style of the formatting.
+ let format = |buf: &mut Formatter, record: &Record| {
+ let mut level_style = buf.style();
+ match record.level() {
+ Level::Warn => {
+ let _ = level_style.set_color(Color::Yellow).set_bold(true);
+ }
+ Level::Error => {
+ let _ = level_style.set_color(Color::Red).set_bold(true);
+ }
+ Level::Debug => {
+ let _ = level_style.set_bold(true);
+ }
+ _ => {}
+ }
+
+ writeln!(
+ buf,
+ "{}: {}",
+ level_style.value(record.level()),
+ record.args()
+ )
+ };
+
+ // Define the logging level for the messages.
+ let log_level = if is_verbose {
+ LevelFilter::Debug
+ } else {
+ LevelFilter::Info
+ };
+
+ let mut builder = Builder::new();
+
+ // Initialize the logger.
+ if let Ok(env_log) = env::var("RUST_LOG") {
+ builder.format(format).parse(&env_log).try_init()
+ } else {
+ builder
+ .format(format)
+ .filter(Some("super"), log_level)
+ .try_init()
+ }
+}
+
+/// Integration and unit tests module.
+///
+/// This module includes tests for the analyzer. It includes both unit tests and
+/// integration tests.
+#[cfg(test)]
+mod tests {
+ extern crate reqwest;
+
+ use std::{collections::BTreeMap, fs, path::Path, str::FromStr};
+
+ use super::analyze_package;
+ use crate::{config::Config, criticality::Criticality};
+
+ /// This tests checks that the `Criticality` enumeration works as expected.
+ ///
+ /// It checks the conversion both from and to strings, the comparisons between
+ /// criticality levels and the debug format.
+ #[test]
+ #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
+ fn it_criticality() {
+ // Check "warnings" from strings
+ assert_eq!(
+ Criticality::from_str("warning").unwrap(),
+ Criticality::Warning
+ );
+ assert_eq!(
+ Criticality::from_str("Warning").unwrap(),
+ Criticality::Warning
+ );
+ assert_eq!(
+ Criticality::from_str("WARNING").unwrap(),
+ Criticality::Warning
+ );
+
+ // Check low criticality from strings.
+ assert_eq!(Criticality::from_str("low").unwrap(), Criticality::Low);
+ assert_eq!(Criticality::from_str("Low").unwrap(), Criticality::Low);
+ assert_eq!(Criticality::from_str("LOW").unwrap(), Criticality::Low);
+
+ // Check medium criticality from strings.
+ assert_eq!(
+ Criticality::from_str("medium").unwrap(),
+ Criticality::Medium
+ );
+ assert_eq!(
+ Criticality::from_str("Medium").unwrap(),
+ Criticality::Medium
+ );
+ assert_eq!(
+ Criticality::from_str("MEDIUM").unwrap(),
+ Criticality::Medium
+ );
+
+ // Check high criticality from strings.
+ assert_eq!(Criticality::from_str("high").unwrap(), Criticality::High);
+ assert_eq!(Criticality::from_str("High").unwrap(), Criticality::High);
+ assert_eq!(Criticality::from_str("HIGH").unwrap(), Criticality::High);
+
+ // Check critical criticality from strings.
+ assert_eq!(
+ Criticality::from_str("critical").unwrap(),
+ Criticality::Critical
+ );
+ assert_eq!(
+ Criticality::from_str("Critical").unwrap(),
+ Criticality::Critical
+ );
+ assert_eq!(
+ Criticality::from_str("CRITICAL").unwrap(),
+ Criticality::Critical
+ );
+
+ // Check that the comparisions between criticalities is correct.
+ assert!(Criticality::Warning < Criticality::Low);
+ assert!(Criticality::Warning < Criticality::Medium);
+ assert!(Criticality::Warning < Criticality::High);
+ assert!(Criticality::Warning < Criticality::Critical);
+ assert!(Criticality::Low < Criticality::Medium);
+ assert!(Criticality::Low < Criticality::High);
+ assert!(Criticality::Low < Criticality::Critical);
+ assert!(Criticality::Medium < Criticality::High);
+ assert!(Criticality::Medium < Criticality::Critical);
+ assert!(Criticality::High < Criticality::Critical);
+
+ // Check that the criticalities are printed correctly with the `Display` trait.
+ assert_eq!(format!("{}", Criticality::Warning).as_str(), "warning");
+ assert_eq!(format!("{}", Criticality::Low).as_str(), "low");
+ assert_eq!(format!("{}", Criticality::Medium).as_str(), "medium");
+ assert_eq!(format!("{}", Criticality::High).as_str(), "high");
+ assert_eq!(format!("{}", Criticality::Critical).as_str(), "critical");
+
+ // Check that the criticalities are printed correctly with the `Debug` trait.
+ assert_eq!(format!("{:?}", Criticality::Warning).as_str(), "Warning");
+ assert_eq!(format!("{:?}", Criticality::Low).as_str(), "Low");
+ assert_eq!(format!("{:?}", Criticality::Medium).as_str(), "Medium");
+ assert_eq!(format!("{:?}", Criticality::High).as_str(), "High");
+ assert_eq!(format!("{:?}", Criticality::Critical).as_str(), "Critical");
+ }
+
+ /// General package analysis test, ignored by default.
+ ///
+ /// This will download an apk from a public repository, analyze it and
+ /// generate the results. It will check that no error gets generated.
+ /// It still does not check that the results are the expected results.
+ #[test]
+ #[ignore]
+ fn it_analyze_package() {
+ let need_to_create = !Path::new("downloads").exists();
+ if need_to_create {
+ fs::create_dir("downloads").unwrap();
+ }
+ // Create the destination file.
+ let mut apk_file = fs::File::create("downloads/test_app.apk").unwrap();
+
+ // TODO: use an application that we control.
+ // Download the .apk fie
+ let _ = reqwest::get(
+ "https://github.com/javiersantos/MLManager/releases/download/v1.0.4.1/\
+ com.javiersantos.mlmanager_1.0.4.1.apk",
+ ).unwrap()
+ .copy_to(&mut apk_file)
+ .unwrap();
+
+ // Initialize minimum configuration.
+ let mut benchmarks = BTreeMap::new();
+ let mut config = Config::from_file("config.toml").unwrap();
+ config.add_app_package("downloads/test_app");
+
+ // Run the analysis
+ analyze_package("downloads/test_app.apk", &mut config, &mut benchmarks).unwrap();
+
+ // TODO: check results.
+
+ // Remove generated files.
+ if need_to_create {
+ fs::remove_dir_all("downloads").unwrap();
+ } else {
+ fs::remove_file("downloads/test_app.apk").unwrap();
+ }
+ // TODO: maybe we should only remove the application specific files.
+ fs::remove_dir_all("dist").unwrap();
+ fs::remove_dir_all("results").unwrap();
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index eeb7b079e..c6d1f51f7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,150 +1,119 @@
-//! SUPER Android Analyzer
+//! SUPER Android Analyzer launcher.
+//!
+//! This code controls the CLI of the application and launches the analysis of the application
+//! using the core library.
+
+#![cfg_attr(feature = "cargo-clippy", deny(clippy))]
+#![forbid(anonymous_parameters)]
+#![cfg_attr(feature = "cargo-clippy", warn(clippy_pedantic))]
+#![cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+#![deny(
+ variant_size_differences,
+ unused_results,
+ unused_qualifications,
+ unused_import_braces,
+ unsafe_code,
+ trivial_numeric_casts,
+ trivial_casts,
+ missing_debug_implementations,
+ missing_docs,
+ missing_copy_implementations
+)]
+
+extern crate super_analyzer_core;
-#![forbid(deprecated, overflowing_literals, stable_features, trivial_casts, unconditional_recursion,
- plugin_as_library, unused_allocation, trivial_numeric_casts, unused_features, while_truem,
- unused_parens, unused_comparisons, unused_extern_crates, unused_import_braces, unused_results,
- improper_ctypes, non_shorthand_field_patterns, private_no_mangle_fns, private_no_mangle_statics,
- filter_map, used_underscore_binding, option_map_unwrap_or, option_map_unwrap_or_else,
- mutex_integer, mut_mut, mem_forget)]
-#![deny(unused_qualifications, unused, unused_attributes)]
-#![warn(missing_docs, variant_size_differences, enum_glob_use, if_not_else,
- invalid_upcast_comparisons, items_after_statements, non_ascii_literal, nonminimal_bool,
- pub_enum_variant_names, shadow_reuse, shadow_same, shadow_unrelated, similar_names,
- single_match_else, string_add, string_add_assign, unicode_not_nfc, unseparated_literal_suffix,
- use_debug, wrong_pub_self_convention, doc_markdown)]
-// Allowing these at least for now.
-#![allow(missing_docs_in_private_items, unknown_lints, print_stdout, stutter, option_unwrap_used,
- result_unwrap_used, integer_arithmetic, cast_possible_truncation, cast_possible_wrap,
- indexing_slicing, cast_precision_loss, cast_sign_loss)]
-
-#[macro_use]
-extern crate clap;
extern crate colored;
-extern crate xml;
-extern crate serde;
-extern crate serde_json;
-extern crate chrono;
-extern crate toml;
-extern crate regex;
-#[macro_use]
-extern crate lazy_static;
-extern crate rustc_serialize;
-extern crate open;
-extern crate bytecount;
-extern crate handlebars;
+extern crate failure;
#[macro_use]
extern crate log;
-extern crate env_logger;
-#[macro_use]
-extern crate error_chain;
-extern crate abxml;
-extern crate md5;
-extern crate sha1;
-extern crate sha2;
-mod error;
-mod cli;
-mod decompilation;
-mod static_analysis;
-mod results;
-mod config;
-mod utils;
+use std::{
+ collections::BTreeMap,
+ io::{self, Write},
+ thread::sleep,
+ time::{Duration, Instant},
+};
-use std::{fs, io, fmt, result};
-use std::path::Path;
-use std::fmt::Display;
-use std::str::FromStr;
-use std::error::Error as StdError;
-use std::io::Write;
-use std::time::{Instant, Duration};
-use std::thread::sleep;
-use std::collections::BTreeMap;
-
-use serde::ser::{Serialize, Serializer};
use colored::Colorize;
+use failure::{Error, ResultExt};
+use log::Level;
-use log::{LogRecord, LogLevelFilter, LogLevel};
-use env_logger::LogBuilder;
-use std::env;
-use decompilation::*;
-use static_analysis::*;
-use results::*;
-use error::*;
-pub use config::Config;
-pub use utils::*;
-
-static BANNER: &'static str = include_str!("banner.txt");
+use super_analyzer_core::{
+ analyze_package, cli, error, initialize_config, initialize_logger, Benchmark, BANNER,
+};
-#[allow(print_stdout)]
+/// Program entry point.
+///
+/// This function will just call the `run()` function and report any fatal error that comes out
+/// of it. It will also exit with a non-zero exit code if things go wrong.
fn main() {
+ // Call the `run()` function and check for errors.
if let Err(e) = run() {
error!("{}", e);
- for e in e.iter().skip(1) {
+ // After printing the error, print the causes, in order.
+ for e in e.iter_causes() {
println!("\t{}{}", "Caused by: ".bold(), e);
}
- if !log_enabled!(LogLevel::Debug) {
+ // If the verbose mode is not enabled, we add a message so that the user knows that can
+ // get further information with the `-v` flag in the CLI.
+ if !log_enabled!(Level::Debug) {
println!(
"If you need more information, try to run the program again with the {} flag.",
"-v".bold()
);
}
- if let Some(backtrace) = e.backtrace() {
- #[allow(use_debug)]
- {
- println!("backtrace: {:?}", backtrace);
- }
- }
-
- ::std::process::exit(e.into());
+ // Exit with a non-zero exit code.
+ ::std::process::exit(1);
}
}
-fn run() -> Result<()> {
+/// Execute the analysis.
+///
+/// This runs the actual analysis. It checks the CLI, creates the logger, loads the configuration
+/// and if everything goes well, it starts the analysis. It also runs benchmarks and shows the
+/// results.
+fn run() -> Result<(), Error> {
+ // Check the CLI arguments.
let cli = cli::generate().get_matches();
let verbose = cli.is_present("verbose");
- initialize_logger(verbose);
+ // Initialize all logger, specifying if the user wanted verbose mode.
+ initialize_logger(verbose).context("could not initialize the logger")?;
- let mut config = match Config::from_cli(cli) {
- Ok(c) => c,
- Err(e) => {
- print_warning(format!(
- "There was an error when reading the config.toml file: {}",
- e.description()
- ));
-
- Config::default()
- }
- };
+ // Load the configuration.
+ let mut config = initialize_config(&cli)?;
+ // Check the configuration and return an error with the loaded files.
if !config.check() {
- let mut error_string = String::from("Configuration errors were found:\n");
- for error in config.get_errors() {
+ let mut error_string = String::from("configuration errors were found:\n");
+ for error in config.errors() {
error_string.push_str(&error);
error_string.push('\n');
}
error_string.push_str(
- "The configuration was loaded, in order, from the following files: \
- \n\t- Default built-in configuration\n",
+ "the configuration was loaded, in order, from the following files: \
+ \n\t- Default built-in configuration\n",
);
- for file in config.get_loaded_config_files() {
+ for file in config.loaded_config_files() {
error_string.push_str(&format!("\t- {}\n", file.display()));
}
- return Err(ErrorKind::Config(error_string).into());
+ return Err(error::Kind::Config {
+ message: error_string,
+ }.into());
}
+ // Print the banner if we are in verbose mode.
if config.is_verbose() {
for c in BANNER.chars() {
print!("{}", c);
- io::stdout().flush().unwrap();
+ io::stdout().flush().expect("error flushing stdout");
sleep(Duration::from_millis(3));
}
println!(
- "Welcome to the SUPER Android Analyzer. We will now try to audit the given \
- application."
+ "Welcome to the SUPER Android Analyzer. We will now try to audit the given application."
);
println!(
"You activated the verbose mode. {}",
@@ -154,15 +123,18 @@ fn run() -> Result<()> {
sleep(Duration::from_millis(1250));
}
+ // Start benchmarks.
let mut benchmarks = BTreeMap::new();
let total_start = Instant::now();
- for package in config.get_app_packages() {
+ // Analyze each apk one by one.
+ for package in config.app_packages() {
config.reset_force();
analyze_package(package, &mut config, &mut benchmarks)
- .chain_err(|| "Application analysis failed")?;
+ .context("application analysis failed")?;
}
+ // Print benchmarks if in benchmark mode.
if config.is_bench() {
let total_time = Benchmark::new("Total time", total_start.elapsed());
println!();
@@ -179,347 +151,3 @@ fn run() -> Result<()> {
Ok(())
}
-
-/// Analyzes the given package with the given config.
-fn analyze_package>(
- package: P,
- config: &mut Config,
- benchmarks: &mut BTreeMap>,
-) -> Result<()> {
- let package_name = get_package_name(&package);
- if config.is_bench() {
- let _ = benchmarks.insert(package_name.clone(), Vec::with_capacity(4));
- }
- if !config.is_quiet() {
- println!();
- println!("Starting analysis of {}.", package_name.italic());
- }
- let start_time = Instant::now();
-
- // Apk decompression
- decompress(config, &package).chain_err(
- || "apk decompression failed",
- )?;
-
- if config.is_bench() {
- benchmarks.get_mut(&package_name).unwrap().push(
- Benchmark::new(
- "Apk decompression",
- start_time
- .elapsed(),
- ),
- );
- }
-
- let dex_jar_time = Instant::now();
- // Converting the .dex to .jar.
- dex_to_jar(config, &package).chain_err(
- || "Conversion from DEX to JAR failed",
- )?;
-
- if config.is_bench() {
- benchmarks.get_mut(&package_name).unwrap().push(
- Benchmark::new(
- "Dex to Jar decompilation (dex2jar Java dependency)",
- dex_jar_time
- .elapsed(),
- ),
- );
- }
-
- if config.is_verbose() {
- println!();
- println!(
- "Now it's time for the actual decompilation of the source code. We'll translate
- Android JVM bytecode to Java, so that we can check the code afterwards."
- );
- }
-
- let decompile_start = Instant::now();
-
- // Decompiling the app
- decompile(config, &package).chain_err(
- || "JAR decompression failed",
- )?;
-
- if config.is_bench() {
- benchmarks.get_mut(&package_name).unwrap().push(
- Benchmark::new(
- "Decompilation (jd-cli Java dependency)",
- decompile_start
- .elapsed(),
- ),
- );
- }
-
- let mut results = Results::init(config, &package)?;
- let static_start = Instant::now();
- // Static application analysis
- static_analysis(config, &package_name, &mut results);
-
- if config.is_bench() {
- benchmarks.get_mut(&package_name).unwrap().push(
- Benchmark::new(
- "Total static analysis",
- static_start
- .elapsed(),
- ),
- );
- }
-
- // TODO dynamic analysis
-
- if !config.is_quiet() {
- println!();
- }
-
- let report_start = Instant::now();
- results.generate_report(config, &package_name).chain_err(
- || "There was an error generating the results report",
- )?;
-
- if config.is_verbose() {
- println!("Everything went smoothly, now you can check all the results.");
- println!();
- println!("I will now analyze myself for vulnerabilities…");
- sleep(Duration::from_millis(1500));
- println!(
- "Nah, just kidding, I've been developed in {}!",
- "Rust".bold().green()
- )
- }
-
- if config.is_bench() {
- benchmarks.get_mut(&package_name).unwrap().push(
- Benchmark::new(
- "Report generation",
- report_start
- .elapsed(),
- ),
- );
- benchmarks.get_mut(&package_name).unwrap().push(
- Benchmark::new(
- format!(
- "Total time for {}",
- package_name
- ),
- start_time
- .elapsed(),
- ),
- );
- }
-
- if config.is_open() {
- let open_path = if config.has_to_generate_html() {
- config
- .get_results_folder()
- .join(results.get_app_package())
- .join("index.html")
- } else {
- config
- .get_results_folder()
- .join(results.get_app_package())
- .join("results.json")
- };
-
- let status = open::that(open_path).chain_err(
- || "Report could not be opened automatically",
- )?;
-
- if !status.success() {
- return Err(
- format!("Report opening errored with status code: {}", status).into(),
- );
- }
- }
-
- Ok(())
-}
-
-/// Vulnerability criticality
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
-pub enum Criticality {
- /// Warning.
- Warning,
- /// Low criticality vulnerability.
- Low,
- /// Medium criticality vulnerability.
- Medium,
- /// High criticality vulnerability.
- High,
- /// Critical vulnerability.
- Critical,
-}
-
-impl Display for Criticality {
- #[allow(use_debug)]
- fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
- write!(f, "{}", format!("{:?}", self).to_lowercase())
- }
-}
-
-impl Serialize for Criticality {
- fn serialize(&self, serializer: S) -> result::Result
- where
- S: Serializer,
- {
- serializer.serialize_str(format!("{}", self).as_str())
- }
-}
-
-impl FromStr for Criticality {
- type Err = Error;
- fn from_str(s: &str) -> Result {
- match s.to_lowercase().as_str() {
- "critical" => Ok(Criticality::Critical),
- "high" => Ok(Criticality::High),
- "medium" => Ok(Criticality::Medium),
- "low" => Ok(Criticality::Low),
- "warning" => Ok(Criticality::Warning),
- _ => Err(ErrorKind::Parse.into()),
- }
- }
-}
-
-/// Copies the contents of `from` to `to`
-///
-/// If the destination folder doesn't exist is created. Note that the parent folder must exist. If
-/// files in the destination folder exist with the same name as in the origin folder, they will be
-/// overwriten.
-pub fn copy_folder>(from: P, to: P) -> Result<()> {
- if !to.as_ref().exists() {
- fs::create_dir(to.as_ref())?;
- }
-
- for f in fs::read_dir(from)? {
- let f = f?;
- if f.path().is_dir() {
- copy_folder(f.path(), to.as_ref().join(f.path().file_name().unwrap()))?;
- } else {
- let _ = fs::copy(f.path(), to.as_ref().join(f.path().file_name().unwrap()))?;
- }
- }
- Ok(())
-}
-
-fn initialize_logger(is_verbose: bool) {
- let format = |record: &LogRecord| match record.level() {
- LogLevel::Warn => {
- format!(
- "{}{}",
- "Warning: ".bold().yellow(),
- record.args().to_string().yellow()
- )
- }
- LogLevel::Error => {
- format!(
- "{}{}",
- "Error: ".bold().red(),
- record.args().to_string().red()
- )
- }
- LogLevel::Debug => format!("{}{}", "Debug: ".bold(), record.args().to_string().bold()),
- LogLevel::Info => format!("{}", record.args()),
- _ => format!("{}: {}", record.level(), record.args()),
- };
-
- let log_level = if is_verbose {
- LogLevelFilter::Debug
- } else {
- LogLevelFilter::Info
- };
-
- let mut builder = LogBuilder::new();
-
- let builder_state = if let Ok(env_log) = env::var("RUST_LOG") {
- builder.format(format).parse(&env_log).init()
- } else {
- builder
- .format(format)
- .filter(Some("super"), log_level)
- .init()
- };
-
- if let Err(e) = builder_state {
- println!("Could not initialize logger: {}", e);
- }
-}
-
-#[cfg(test)]
-mod tests {
- use Criticality;
- use std::str::FromStr;
-
- #[test]
- fn it_criticality() {
- assert_eq!(
- Criticality::from_str("warning").unwrap(),
- Criticality::Warning
- );
- assert_eq!(
- Criticality::from_str("Warning").unwrap(),
- Criticality::Warning
- );
- assert_eq!(
- Criticality::from_str("WARNING").unwrap(),
- Criticality::Warning
- );
-
- assert_eq!(Criticality::from_str("low").unwrap(), Criticality::Low);
- assert_eq!(Criticality::from_str("Low").unwrap(), Criticality::Low);
- assert_eq!(Criticality::from_str("LOW").unwrap(), Criticality::Low);
-
- assert_eq!(
- Criticality::from_str("medium").unwrap(),
- Criticality::Medium
- );
- assert_eq!(
- Criticality::from_str("Medium").unwrap(),
- Criticality::Medium
- );
- assert_eq!(
- Criticality::from_str("MEDIUM").unwrap(),
- Criticality::Medium
- );
-
- assert_eq!(Criticality::from_str("high").unwrap(), Criticality::High);
- assert_eq!(Criticality::from_str("High").unwrap(), Criticality::High);
- assert_eq!(Criticality::from_str("HIGH").unwrap(), Criticality::High);
-
- assert_eq!(
- Criticality::from_str("critical").unwrap(),
- Criticality::Critical
- );
- assert_eq!(
- Criticality::from_str("Critical").unwrap(),
- Criticality::Critical
- );
- assert_eq!(
- Criticality::from_str("CRITICAL").unwrap(),
- Criticality::Critical
- );
-
- assert!(Criticality::Warning < Criticality::Low);
- assert!(Criticality::Warning < Criticality::Medium);
- assert!(Criticality::Warning < Criticality::High);
- assert!(Criticality::Warning < Criticality::Critical);
- assert!(Criticality::Low < Criticality::Medium);
- assert!(Criticality::Low < Criticality::High);
- assert!(Criticality::Low < Criticality::Critical);
- assert!(Criticality::Medium < Criticality::High);
- assert!(Criticality::Medium < Criticality::Critical);
- assert!(Criticality::High < Criticality::Critical);
-
- assert_eq!(format!("{}", Criticality::Warning).as_str(), "warning");
- assert_eq!(format!("{}", Criticality::Low).as_str(), "low");
- assert_eq!(format!("{}", Criticality::Medium).as_str(), "medium");
- assert_eq!(format!("{}", Criticality::High).as_str(), "high");
- assert_eq!(format!("{}", Criticality::Critical).as_str(), "critical");
-
- assert_eq!(format!("{:?}", Criticality::Warning).as_str(), "Warning");
- assert_eq!(format!("{:?}", Criticality::Low).as_str(), "Low");
- assert_eq!(format!("{:?}", Criticality::Medium).as_str(), "Medium");
- assert_eq!(format!("{:?}", Criticality::High).as_str(), "High");
- assert_eq!(format!("{:?}", Criticality::Critical).as_str(), "Critical");
- }
-}
diff --git a/src/results/handlebars_helpers.rs b/src/results/handlebars_helpers.rs
index 6e4fb2755..b474cf6a1 100644
--- a/src/results/handlebars_helpers.rs
+++ b/src/results/handlebars_helpers.rs
@@ -1,24 +1,29 @@
-use std::io::Write;
-
-use handlebars::{Helper, Handlebars, RenderContext, RenderError};
-use serde_json::Value;
use bytecount::count;
+use handlebars::{Context, Handlebars as Registry, Helper, Output, RenderContext, RenderError};
+use serde_json::Value;
-use super::utils::{split_indent, html_escape};
+use super::utils::{html_escape, split_indent};
/// Generates a list of line numbers for the given vulnerability.
///
/// An optional line separator can be added that will be used at the end of each line. By default,
/// this separator will be ` `.
-pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
- let vulnerability = h.param(0).and_then(|v| v.value().as_object()).ok_or_else(
- || {
+pub fn line_numbers(
+ h: &Helper,
+ _: &Registry,
+ _: &Context,
+ _: &mut RenderContext,
+ out: &mut Output,
+) -> Result<(), RenderError> {
+ let vulnerability = h
+ .param(0)
+ .and_then(|v| v.value().as_object())
+ .ok_or_else(|| {
RenderError::new(
"to generate the vulnerability index, the first parameter must be a \
- vulnerability",
+ vulnerability",
)
- },
- )?;
+ })?;
let line_separator = match h.param(1) {
Some(s) => {
if let Value::String(ref s) = *s.value() {
@@ -26,7 +31,7 @@ pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul
} else {
return Err(RenderError::new(
"the provided line separator for the code lines was \
- not a string",
+ not a string",
));
}
}
@@ -44,14 +49,13 @@ pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul
let iter_start = if start_line > 5 { start_line - 4 } else { 1 };
let iter_end = end_line + 5;
- let mut rendered = String::with_capacity(
- (line_separator.len() + 1) * (iter_end - iter_start) as usize,
- );
+ let mut rendered =
+ String::with_capacity((line_separator.len() + 1) * (iter_end - iter_start) as usize);
for l in iter_start..iter_end {
rendered.push_str(&format!("{}", l));
rendered.push_str(line_separator);
}
- let _ = rc.writer.write(rendered.as_bytes())?;
+ out.write(&rendered)?;
Ok(())
}
@@ -60,10 +64,17 @@ pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul
///
/// An optional line separator can be added that will be used at the end of each line. By default,
/// this separator will be ` `.
-pub fn all_lines(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
- let code = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| {
- RenderError::new("the code must be a string")
- })?;
+pub fn all_lines(
+ h: &Helper,
+ _: &Registry,
+ _: &Context,
+ _: &mut RenderContext,
+ out: &mut Output,
+) -> Result<(), RenderError> {
+ let code = h
+ .param(0)
+ .and_then(|v| v.value().as_str())
+ .ok_or_else(|| RenderError::new("the code must be a string"))?;
let line_separator = match h.param(1) {
Some(s) => {
if let Value::String(ref s) = *s.value() {
@@ -71,7 +82,7 @@ pub fn all_lines(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
} else {
return Err(RenderError::new(
"the provided line separator for the code lines was \
- not a string",
+ not a string",
));
}
}
@@ -80,11 +91,11 @@ pub fn all_lines(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
let line_count = count(code.as_bytes(), b'\n');
let mut rendered = String::with_capacity((line_separator.len() + 1) * line_count);
- for l in 1..line_count + 1 {
- rendered.push_str(&format!("{}", l));
+ for l in 1..=line_count {
+ rendered.push_str(format!("{}", l).as_str());
rendered.push_str(line_separator);
}
- let _ = rc.writer.write(rendered.as_bytes())?;
+ out.write(&rendered)?;
Ok(())
}
@@ -93,10 +104,17 @@ pub fn all_lines(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
///
/// An optional line separator can be added that will be used at the end of each line. By default,
/// this separator will be ` `.
-pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
- let code = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| {
- RenderError::new("the code must be a string")
- })?;
+pub fn all_code(
+ h: &Helper,
+ _: &Registry,
+ _: &Context,
+ _: &mut RenderContext,
+ out: &mut Output,
+) -> Result<(), RenderError> {
+ let code = h
+ .param(0)
+ .and_then(|v| v.value().as_str())
+ .ok_or_else(|| RenderError::new("the code must be a string"))?;
let line_separator = match h.param(1) {
Some(s) => {
if let Value::String(ref s) = *s.value() {
@@ -104,7 +122,7 @@ pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<()
} else {
return Err(RenderError::new(
"the provided line separator for the code lines was \
- not a string",
+ not a string",
));
}
}
@@ -115,13 +133,13 @@ pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<()
let (indent, line) = split_indent(line);
let line = format!(
"{}{}{}",
+ class=\"line_body\">{}{}",
i + 1,
indent,
html_escape(line),
line_separator
);
- let _ = rc.writer.write(line.as_bytes())?;
+ out.write(&line)?;
}
Ok(())
@@ -138,15 +156,22 @@ pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<()
/// ```
///
/// This enables easy styling of the code in templates.
-pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
- let vulnerability = h.param(0).and_then(|v| v.value().as_object()).ok_or_else(
- || {
+pub fn html_code(
+ h: &Helper,
+ _: &Registry,
+ _: &Context,
+ _: &mut RenderContext,
+ out: &mut Output,
+) -> Result<(), RenderError> {
+ let vulnerability = h
+ .param(0)
+ .and_then(|v| v.value().as_object())
+ .ok_or_else(|| {
RenderError::new(
"to generate the vulnerability index, the first parameter must be a \
- vulnerability",
+ vulnerability",
)
- },
- )?;
+ })?;
let line_separator = match h.param(1) {
Some(s) => {
if let Value::String(ref s) = *s.value() {
@@ -154,7 +179,7 @@ pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
} else {
return Err(RenderError::new(
"the provided line separator for the code lines was \
- not a string",
+ not a string",
));
}
}
@@ -185,7 +210,7 @@ pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
let (indent, code) = split_indent(line);
format!(
"{}{}{}",
+ class=\"line_body\">{}{}",
vulnerability.get("criticality").unwrap().as_str().unwrap(),
indent,
html_escape(code),
@@ -195,7 +220,7 @@ pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
format!("{}{}", html_escape(line), line_separator)
};
- let _ = rc.writer.write(rendered.as_bytes())?;
+ out.write(&rendered)?;
}
Ok(())
@@ -205,21 +230,29 @@ pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(
///
/// E.g.: for a critical vulnerability in an application with between 100 and 200 vulnerability,
/// for the critical vulnerability number 12 it would produce `C012`.
-pub fn report_index(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
- let vulnerability = h.param(0).and_then(|v| v.value().as_object()).ok_or_else(
- || {
+pub fn report_index(
+ h: &Helper,
+ _: &Registry,
+ _: &Context,
+ _: &mut RenderContext,
+ out: &mut Output,
+) -> Result<(), RenderError> {
+ let vulnerability = h
+ .param(0)
+ .and_then(|v| v.value().as_object())
+ .ok_or_else(|| {
RenderError::new(
"to generate the vulnerability index, the first parameter must be a \
- vulnerability",
+ vulnerability",
)
- },
- )?;
+ })?;
let index = h.param(1).and_then(|v| v.value().as_u64()).ok_or_else(|| {
RenderError::new(
"the index of the vulnerability in the current list must be the \
- second parameter",
+ second parameter",
)
- })? as usize + 1;
+ })? as usize
+ + 1;
let list_len = h.param(2).unwrap().value().as_u64().unwrap();
let char_index = vulnerability
@@ -237,71 +270,70 @@ pub fn report_index(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul
index_padding = 2;
}
let rendered = format!("{}{:#02$}", char_index, index, index_padding);
- let _ = rc.writer.write(rendered.as_bytes())?;
+ out.write(&rendered)?;
Ok(())
}
/// Generates the menu for the source tree.
///
-/// It will generaten unordered HTML list (`
...
`) where all files and folders of the given
+/// It will generate an unordered HTML list (`
…
`) where all files and folders of the given
/// menu object.
pub fn generate_menu(
h: &Helper,
- _: &Handlebars,
- rc: &mut RenderContext,
+ _: &Registry,
+ _: &Context,
+ _: &mut RenderContext,
+ out: &mut Output,
) -> Result<(), RenderError> {
- let menu = h.param(0).and_then(|m| m.value().as_array()).ok_or_else(
- || {
- RenderError::new(
- "to generate the menu, the first parameter must be a menu array",
- )
- },
- )?;
- let _ = rc.writer.write(b"
")?;
- render_menu(menu, &mut rc.writer)?;
- let _ = rc.writer.write(b"
")?;
+ let menu = h
+ .param(0)
+ .and_then(|m| m.value().as_array())
+ .ok_or_else(|| {
+ RenderError::new("to generate the menu, the first parameter must be a menu array")
+ })?;
+ out.write("
")?;
+ render_menu(menu, out)?;
+ out.write("
")?;
Ok(())
}
-fn render_menu(menu: &[Value], renderer: &mut W) -> Result<(), RenderError> {
+fn render_menu(menu: &[Value], renderer: &mut Output) -> Result<(), RenderError> {
for value in menu {
if let Value::Object(ref item) = *value {
- let _ = renderer.write(b"
")?;
- let name = item.get("name").and_then(|n| n.as_str()).ok_or_else(|| {
- RenderError::new("invalid menu object type")
- })?;
+ renderer.write("
")?;
+ let name = item
+ .get("name")
+ .and_then(|n| n.as_str())
+ .ok_or_else(|| RenderError::new("invalid menu object type"))?;
if let Some(&Value::Array(ref menu)) = item.get("menu") {
- let _ = renderer.write(
+ renderer.write(
format!(
- "{0}",
+ "{0}",
name
- ).as_bytes(),
+ ).as_str(),
)?;
- let _ = renderer.write(b"
")?;
+ renderer.write("
")?;
render_menu(menu, renderer)?;
- let _ = renderer.write(b"
")?;
+ renderer.write("
")?;
} else {
- let path = item.get("path").and_then(|n| n.as_str()).ok_or_else(|| {
- RenderError::new("invalid menu object type")
- })?;
- let file_type = item.get("type").and_then(|n| n.as_str()).ok_or_else(|| {
- RenderError::new("invalid menu object type")
- })?;
- let _ = renderer.write(
+ let path = item
+ .get("path")
+ .and_then(|n| n.as_str())
+ .ok_or_else(|| RenderError::new("invalid menu object type"))?;
+ let file_type = item
+ .get("type")
+ .and_then(|n| n.as_str())
+ .ok_or_else(|| RenderError::new("invalid menu object type"))?;
+ renderer.write(
format!(
- "{0}",
- name,
- path,
- file_type
- ).as_bytes(),
+ "{0}",
+ name, path, file_type
+ ).as_str()
)?;
}
- let _ = renderer.write(b"
")?;
+ renderer.write("")?;
} else {
return Err(RenderError::new("invalid menu object type"));
}
diff --git a/src/results/mod.rs b/src/results/mod.rs
index f707794aa..573e12ccf 100644
--- a/src/results/mod.rs
+++ b/src/results/mod.rs
@@ -1,51 +1,72 @@
-use std::fs;
-use std::collections::BTreeSet;
-use std::path::Path;
-use std::result::Result as StdResult;
-use std::error::Error as StdError;
+//! Results generation module.
+
+use std::{collections::BTreeSet, fs, path::Path};
-use serde::ser::{Serialize, SerializeStruct, Serializer};
use chrono::Local;
+use clap::crate_version;
+use failure::{Error, ResultExt};
+use serde::ser::{Serialize, SerializeStruct, Serializer};
-mod utils;
mod handlebars_helpers;
mod report;
+mod sdk_number;
+mod utils;
-pub use self::utils::{Vulnerability, split_indent, html_escape};
-use self::utils::FingerPrint;
-
-use error::*;
-use {Config, Criticality, print_warning};
-
-use results::report::{Json, HandlebarsReport};
-use results::report::Report;
-
+pub use self::utils::{html_escape, split_indent, Vulnerability};
+use self::{
+ sdk_number::{prettify_android_version, SdkNumber},
+ utils::FingerPrint,
+};
+use crate::{
+ criticality::Criticality,
+ print_warning,
+ results::report::{Generator, HandlebarsReport, Json},
+ Config,
+};
+
+/// Results representation structure.
pub struct Results {
+ /// Application package name.
app_package: String,
+ /// Application label.
app_label: String,
+ /// Application description.
app_description: String,
+ /// Application version string.
app_version: String,
+ /// Application version number.
app_version_num: u32,
- app_min_sdk: u32,
- app_target_sdk: Option,
+ /// Application minimum SDK.
+ app_min_sdk: SdkNumber,
+ /// Target SDK for the application.
+ app_target_sdk: Option,
+ /// Fingerprint of the application,
app_fingerprint: FingerPrint,
+ /// Certificate of the application.
#[cfg(feature = "certificate")]
certificate: String,
+ /// List of warnings found in the application.
warnings: BTreeSet,
+ /// List of the potential low criticality vulnerabilities in the application.
low: BTreeSet,
+ /// List of the potential medium criticality vulnerabilities in the application.
medium: BTreeSet,
+ /// List of the potential high criticality vulnerabilities in the application.
high: BTreeSet,
+ /// List of the potential critical vulnerabilities in the application.
critical: BTreeSet,
}
impl Results {
- pub fn init>(config: &Config, package: P) -> Result {
+ /// Initializes the results structure.
+ #[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+ pub fn init>(config: &Config, package: P) -> Result {
let fingerprint = match FingerPrint::new(package) {
Ok(f) => f,
Err(e) => {
print_warning(format!(
"An error occurred when trying to fingerprint the \
- application: {}",
+ application: {}",
e
));
return Err(e)?;
@@ -54,22 +75,22 @@ impl Results {
if config.is_verbose() {
println!(
"The results struct has been created. All the vulnerabilitis will now \
- be recorded and when the analysis ends, they will be written to result \
- files."
+ be recorded and when the analysis ends, they will be written to result \
+ files."
);
} else if !config.is_quiet() {
- println!("Results struct created.");
+ println!("Results structure created.");
}
#[cfg(feature = "certificate")]
{
- Ok(Results {
+ Ok(Self {
app_package: String::new(),
app_label: String::new(),
app_description: String::new(),
app_version: String::new(),
app_version_num: 0,
- app_min_sdk: 0,
+ app_min_sdk: SdkNumber::Unknown(0),
app_target_sdk: None,
app_fingerprint: fingerprint,
certificate: String::new(),
@@ -83,13 +104,13 @@ impl Results {
#[cfg(not(feature = "certificate"))]
{
- Ok(Results {
+ Ok(Self {
app_package: String::new(),
app_label: String::new(),
app_description: String::new(),
app_version: String::new(),
app_version_num: 0,
- app_min_sdk: 0,
+ app_min_sdk: SdkNumber::Unknown(0),
app_target_sdk: None,
app_fingerprint: fingerprint,
warnings: BTreeSet::new(),
@@ -101,65 +122,100 @@ impl Results {
}
}
+ /// Sets the application's package.
pub fn set_app_package>(&mut self, package: S) {
self.app_package = package.into();
}
- pub fn get_app_package(&self) -> &str {
+ /// Gets the application package.
+ pub fn app_package(&self) -> &str {
&self.app_package
}
+ /// Sets the certificate string.
#[cfg(feature = "certificate")]
pub fn set_certificate>(&mut self, certificate: S) {
self.certificate = certificate.into();
}
+ /// Sets the application's label.
pub fn set_app_label>(&mut self, label: S) {
self.app_label = label.into();
}
+ /// Sets the application description
pub fn set_app_description>(&mut self, description: S) {
self.app_description = description.into();
}
+ /// Sets the application version string.
pub fn set_app_version>(&mut self, version: S) {
self.app_version = version.into();
}
+ /// Sets the application version number.
pub fn set_app_version_num(&mut self, version: u32) {
self.app_version_num = version;
}
+ /// Sets the application's minimum SDK number.
pub fn set_app_min_sdk(&mut self, sdk: u32) {
- self.app_min_sdk = sdk;
+ self.app_min_sdk = SdkNumber::from(sdk);
}
+ /// Sets the application's target SDK number.
pub fn set_app_target_sdk(&mut self, sdk: u32) {
- self.app_target_sdk = Some(sdk);
+ self.app_target_sdk = Some(SdkNumber::from(sdk));
}
+ /// Adds a vulnerability to the results.
+ #[allow(unused_variables)] // Until we remove the debug assertions
pub fn add_vulnerability(&mut self, vuln: Vulnerability) {
match vuln.get_criticality() {
Criticality::Warning => {
- self.warnings.insert(vuln);
+ let new = self.warnings.insert(vuln);
+ // FIXME should we maintain it?
+ //debug_assert!(new, "trying to insert the same warning twice");
}
Criticality::Low => {
- self.low.insert(vuln);
+ let new = self.low.insert(vuln);
+ // FIXME should we maintain it?
+ // debug_assert!(
+ // new,
+ // "trying to insert the same low criticality vulnerability twice"
+ // );
}
Criticality::Medium => {
- self.medium.insert(vuln);
+ let new = self.medium.insert(vuln);
+ // FIXME should we maintain it?
+ // debug_assert!(
+ // new,
+ // "trying to insert the same medium criticalityvulnerability twice"
+ // );
}
Criticality::High => {
- self.high.insert(vuln);
+ let new = self.high.insert(vuln);
+ // FIXME should we maintain it?
+ // debug_assert!(
+ // new,
+ // "trying to insert the same high criticality vulnerability twice"
+ // );
}
Criticality::Critical => {
- self.critical.insert(vuln);
+ let new = self.critical.insert(vuln.clone());
+ // FIXME should we maintain it?
+ // debug_assert!(
+ // new,
+ // "trying to insert the same critical vulnerability twice"
+ // );
}
}
}
- pub fn generate_report>(&self, config: &Config, package: S) -> Result<()> {
- let path = config.get_results_folder().join(&self.app_package);
+ /// Generates the report.
+ #[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+ pub fn generate_report>(&self, config: &Config, package: S) -> Result<(), Error> {
+ let path = config.results_folder().join(&self.app_package);
if config.is_verbose() {
println!("Starting report generation.");
}
@@ -183,30 +239,27 @@ impl Results {
if let Err(e) = fs::remove_file(&path) {
print_warning(format!(
- "There was an error when removing the JSON results \
- file: {}",
- e.description()
+ "there was an error when removing the JSON results file: {}",
+ e
));
}
}
let mut json_reporter = Json::new();
if let Err(e) = json_reporter.generate(config, self) {
- print_warning(format!("There was en error generating JSON report: {}", e));
+ print_warning(format!("there was en error generating JSON report: {}", e));
}
if !config.is_quiet() {
println!("JSON report generated.");
}
+ } else if config.is_verbose() {
+ println!(
+ "Seems that the JSON report has already been generated. There is no \
+ need to do it again."
+ );
} else {
- if config.is_verbose() {
- println!(
- "Seems that the JSON report has already been generated. There is no \
- need to do it again."
- );
- } else {
- println!("Skipping JSON report generation.");
- }
+ println!("Skipping JSON report generation.");
}
}
@@ -219,26 +272,23 @@ impl Results {
println!("The application HTML resulsts exist. But no more…");
}
- for f in fs::read_dir(path).chain_err(
- || "there was an error when removing the HTML results",
- )?
+ for f in fs::read_dir(path)
+ .context("there was an error when removing the HTML results")?
{
let f = f?;
if f.file_type()?.is_dir() {
- fs::remove_dir_all(f.path()).chain_err(
- || "there was an error when removing the HTML results",
- )?;
+ fs::remove_dir_all(f.path())
+ .context("there was an error when removing the HTML results")?;
} else if &f.file_name() != "results.json" {
- fs::remove_file(f.path()).chain_err(
- || "there was an error when removing the HTML results",
- )?;
+ fs::remove_file(f.path())
+ .context("there was an error when removing the HTML results")?;
}
}
}
let handelbars_report_result =
- HandlebarsReport::new(config.get_template_path(), package.as_ref().to_owned());
+ HandlebarsReport::new(config.template_path(), package.as_ref().to_owned());
if let Ok(mut handlebars_reporter) = handelbars_report_result {
if let Err(e) = handlebars_reporter.generate(config, self) {
@@ -249,15 +299,13 @@ impl Results {
println!("HTML report generated.");
}
}
+ } else if config.is_verbose() {
+ println!(
+ "Seems that the HTML report has already been generated. There is no
+ need to do it again."
+ );
} else {
- if config.is_verbose() {
- println!(
- "Seems that the HTML report has already been generated. There is no
- need to do it again."
- );
- } else {
- println!("Skipping HTML report generation.");
- }
+ println!("Skipping HTML report generation.");
}
}
@@ -266,74 +314,81 @@ impl Results {
}
impl Serialize for Results {
- fn serialize(&self, serializer: S) -> StdResult
+ fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
{
let now = Local::now();
- let mut ser_struct = serializer.serialize_struct(
- "Results",
+ let len = {
+ let mut len = 21;
if cfg!(feature = "certificate") {
- 22
- } else {
- 21
- },
- )?;
+ len += 1;
+ }
+ if self.app_min_sdk.version().is_some() {
+ len += 1;
+ }
+ if let Some(target) = self.app_target_sdk {
+ if target.version().is_some() {
+ len += 3;
+ } else {
+ len += 2;
+ }
+ }
+ len
+ };
+ let mut ser_struct = serializer.serialize_struct("Results", len)?;
- ser_struct.serialize_field(
- "super_version",
- crate_version!(),
- )?;
+ ser_struct.serialize_field("super_version", crate_version!())?;
ser_struct.serialize_field("now", &now)?;
ser_struct.serialize_field("now_rfc2822", &now.to_rfc2822())?;
ser_struct.serialize_field("now_rfc3339", &now.to_rfc3339())?;
ser_struct.serialize_field("app_package", &self.app_package)?;
ser_struct.serialize_field("app_version", &self.app_version)?;
- ser_struct.serialize_field(
- "app_version_number",
- &self.app_version_num,
- )?;
- ser_struct.serialize_field(
- "app_fingerprint",
- &self.app_fingerprint,
- )?;
+ ser_struct.serialize_field("app_version_number", &self.app_version_num)?;
+ ser_struct.serialize_field("app_fingerprint", &self.app_fingerprint)?;
#[cfg(feature = "certificate")]
{
ser_struct.serialize_field("certificate", &self.certificate)?;
}
- ser_struct.serialize_field("app_min_sdk", &self.app_min_sdk)?;
- ser_struct.serialize_field(
- "app_target_sdk",
- &self.app_target_sdk,
- )?;
+ ser_struct.serialize_field("app_min_sdk_number", &self.app_min_sdk.number())?;
+
+ ser_struct.serialize_field("app_min_sdk_name", self.app_min_sdk.name())?;
+
+ if let Some(version) = self.app_min_sdk.version() {
+ ser_struct
+ .serialize_field("app_min_sdk_version", &prettify_android_version(&version))?;
+ }
+
+ if let Some(sdk) = self.app_target_sdk {
+ ser_struct.serialize_field("app_target_sdk_number", &sdk.number())?;
+
+ ser_struct.serialize_field("app_target_sdk_name", sdk.name())?;
+
+ if let Some(version) = sdk.version() {
+ ser_struct.serialize_field(
+ "app_target_sdk_version",
+ &prettify_android_version(&version),
+ )?;
+ }
+ }
ser_struct.serialize_field(
"total_vulnerabilities",
- &(self.low.len() + self.medium.len() + self.high.len() +
- self.critical.len()),
+ &(self.low.len() + self.medium.len() + self.high.len() + self.critical.len()),
)?;
ser_struct.serialize_field("criticals", &self.critical)?;
- ser_struct.serialize_field(
- "criticals_len",
- &self.critical.len(),
- )?;
+ ser_struct.serialize_field("criticals_len", &self.critical.len())?;
ser_struct.serialize_field("highs", &self.high)?;
ser_struct.serialize_field("highs_len", &self.high.len())?;
ser_struct.serialize_field("mediums", &self.medium)?;
- ser_struct.serialize_field(
- "mediums_len",
- &self.medium.len(),
- )?;
+ ser_struct.serialize_field("mediums_len", &self.medium.len())?;
ser_struct.serialize_field("lows", &self.low)?;
ser_struct.serialize_field("lows_len", &self.low.len())?;
ser_struct.serialize_field("warnings", &self.warnings)?;
- ser_struct.serialize_field(
- "warnings_len",
- &self.warnings.len(),
- )?;
+ ser_struct.serialize_field("warnings_len", &self.warnings.len())?;
ser_struct.end()
}
diff --git a/src/results/report/handlebars.rs b/src/results/report/handlebars.rs
index c80da660b..1ed14f6a6 100644
--- a/src/results/report/handlebars.rs
+++ b/src/results/report/handlebars.rs
@@ -1,41 +1,55 @@
-use std::path::PathBuf;
-use results::report::Report;
-use results::Results;
-use config::Config;
-use std::io::{Read, Write};
-use std::fs::File;
-use handlebars::Handlebars;
-use results::utils::html_escape;
-use results::handlebars_helpers::*;
-use std::fs;
-use copy_folder;
+//! Handlebars report generation module.
+
+use std::{
+ collections::BTreeMap,
+ fs::{self, File},
+ io::Write,
+ path::Path,
+};
+
use colored::Colorize;
-use serde_json::Map;
-use serde_json::value::Value;
-use std::collections::BTreeMap;
-use std::path::Path;
-use error::*;
+use failure::{Error, ResultExt};
+use handlebars::Handlebars;
+use serde_json::{value::Value, Map};
+
+use crate::{
+ config::Config,
+ copy_folder, error,
+ results::{
+ handlebars_helpers::{
+ all_code, all_lines, generate_menu, html_code, line_numbers, report_index,
+ },
+ report::Generator,
+ utils::html_escape,
+ Results,
+ },
+};
-pub struct HandlebarsReport {
+/// Handlebars report generator.
+pub struct Report {
+ /// Handlebars template structure.
handler: Handlebars,
+ /// Package name.
package: String,
}
-impl HandlebarsReport {
- pub fn new(template_path: PathBuf, package: String) -> Result {
- let handlebars_handler = Self::load_templates(template_path).chain_err(
- || "Could not load handlebars templates",
- )?;
+impl Report {
+ /// Creates a new handlebars report generator.
+ pub fn new, S: Into>(
+ template_path: P,
+ package: S,
+ ) -> Result {
+ let handlebars_handler =
+ Self::load_templates(template_path).context("Could not load handlebars templates")?;
- let report = HandlebarsReport {
+ Ok(Self {
handler: handlebars_handler,
- package: package,
- };
-
- Ok(report)
+ package: package.into(),
+ })
}
- fn load_templates(template_path: PathBuf) -> Result {
+ /// Loads templates from the given path.
+ fn load_templates>(template_path: P) -> Result {
let mut handlebars = Handlebars::new();
handlebars.register_escape_fn(|s| html_escape(s).into_owned());
let _ = handlebars.register_helper("line_numbers", Box::new(line_numbers));
@@ -49,31 +63,26 @@ impl HandlebarsReport {
if let Some(ext) = dir_entry.path().extension() {
if ext == "hbs" {
let path = dir_entry.path();
- let template_file = path.file_stem()
- .ok_or_else(|| {
- ErrorKind::TemplateName(
- "template files must have a file name".to_string(),
- )
- })
- .and_then(|stem| {
- stem.to_str().ok_or_else(|| {
- ErrorKind::TemplateName(
- "template names must be unicode".to_string(),
- )
+ let template_file = path
+ .file_stem()
+ .ok_or_else(|| error::Kind::TemplateName {
+ message: "template files must have a file name".to_owned(),
+ }).and_then(|stem| {
+ stem.to_str().ok_or_else(|| error::Kind::TemplateName {
+ message: "template names must be unicode".to_string(),
})
})?;
-
handlebars
.register_template_file(template_file, dir_entry.path())
- .chain_err(|| "Error registering template file")?;
+ .context("error registering template file")?;
}
}
}
- if handlebars.get_template("report").is_none() ||
- handlebars.get_template("src").is_none() ||
- handlebars.get_template("code").is_none()
+ if handlebars.get_template("report").is_none()
+ || handlebars.get_template("src").is_none()
+ || handlebars.get_template("code").is_none()
{
let message = format!(
"templates must include {}, {} and {} templates",
@@ -82,19 +91,20 @@ impl HandlebarsReport {
"code".italic()
);
- Err(ErrorKind::TemplateName(message).into())
+ Err(error::Kind::TemplateName { message }.into())
} else {
Ok(handlebars)
}
}
- fn generate_code_html_files(&self, config: &Config, results: &Results) -> Result<()> {
+ /// Generates the HTML files for the code.
+ fn generate_code_html_files(&self, config: &Config, results: &Results) -> Result<(), Error> {
let menu = Value::Array(self.generate_code_html_folder("", config, results)?);
let mut f = File::create(
config
- .get_results_folder()
- .join(&results.get_app_package())
+ .results_folder()
+ .join(&results.app_package())
.join("src")
.join("index.html"),
)?;
@@ -106,26 +116,25 @@ impl HandlebarsReport {
Ok(())
}
+ /// Generates a folder with HTML files with the source code of the application.
fn generate_code_html_folder>(
&self,
path: P,
config: &Config,
results: &Results,
- ) -> Result> {
- if path.as_ref() == Path::new("classes/android") ||
- path.as_ref() == Path::new("classes/com/google/android/gms") ||
- path.as_ref() == Path::new("smali")
+ ) -> Result, Error> {
+ if path.as_ref() == Path::new("classes/android")
+ || path.as_ref() == Path::new("classes/com/google/android/gms")
+ || path.as_ref() == Path::new("smali")
{
return Ok(Vec::new());
}
- let dir_iter = fs::read_dir(config.get_dist_folder().join(&self.package).join(
- path.as_ref(),
- ))?;
+ let dir_iter = fs::read_dir(config.dist_folder().join(&self.package).join(path.as_ref()))?;
fs::create_dir_all(
config
- .get_results_folder()
- .join(&results.get_app_package())
+ .results_folder()
+ .join(&results.app_package())
.join("src")
.join(path.as_ref()),
)?;
@@ -135,16 +144,18 @@ impl HandlebarsReport {
let entry = entry?;
let path = entry.path();
- let prefix = config.get_dist_folder().join(&self.package);
- let stripped = path.strip_prefix(&prefix).unwrap();
+ let prefix = config.dist_folder().join(&self.package);
+ let stripped = path
+ .strip_prefix(&prefix)
+ .expect("could not remove path prefix");
if path.is_dir() {
if stripped != Path::new("original") {
let inner_menu = self.generate_code_html_folder(stripped, config, results)?;
if inner_menu.is_empty() {
let path = config
- .get_results_folder()
- .join(&results.get_app_package())
+ .results_folder()
+ .join(&results.app_package())
.join("src")
.join(stripped);
if path.exists() {
@@ -162,12 +173,7 @@ impl HandlebarsReport {
} else {
match path.extension() {
Some(e) if e == "xml" || e == "java" => {
- self.generate_code_html_for(
- &stripped,
- config,
- results,
- &self.package,
- )?;
+ self.generate_code_html_for(&stripped, config, results, &self.package)?;
let name = path.file_name().unwrap().to_string_lossy().into_owned();
let mut data = Map::with_capacity(3);
let _ = data.insert("name".to_owned(), Value::String(name));
@@ -189,32 +195,30 @@ impl HandlebarsReport {
Ok(menu)
}
+ /// Generates an HTML file with source code for the given path.
fn generate_code_html_for, S: AsRef>(
&self,
path: P,
config: &Config,
results: &Results,
cli_package_name: S,
- ) -> Result<()> {
- let mut f_in = File::open(
+ ) -> Result<(), Error> {
+ let code = fs::read_to_string(
config
- .get_dist_folder()
+ .dist_folder()
.join(cli_package_name.as_ref())
.join(path.as_ref()),
)?;
let mut f_out = File::create(format!(
"{}.html",
config
- .get_results_folder()
- .join(&results.get_app_package())
+ .results_folder()
+ .join(&results.app_package())
.join("src")
.join(path.as_ref())
.display()
))?;
- let mut code = String::new();
- let _ = f_in.read_to_string(&mut code)?;
-
let mut back_path = String::new();
for _ in path.as_ref().components() {
back_path.push_str("../");
@@ -228,22 +232,21 @@ impl HandlebarsReport {
let _ = data.insert(String::from("code"), Value::String(code));
let _ = data.insert(String::from("back_path"), Value::String(back_path));
- f_out.write_all(
- self.handler.render("code", &data)?.as_bytes(),
- )?;
+ f_out.write_all(self.handler.render("code", &data)?.as_bytes())?;
Ok(())
}
}
-impl Report for HandlebarsReport {
- fn generate(&mut self, config: &Config, results: &Results) -> Result<()> {
+impl Generator for Report {
+ #[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+ fn generate(&mut self, config: &Config, results: &Results) -> Result<(), Error> {
if config.is_verbose() {
println!("Starting HTML report generation. First we create the file.")
}
let mut f = File::create(
config
- .get_results_folder()
+ .results_folder()
.join(&results.app_package)
.join("index.html"),
)?;
@@ -251,19 +254,17 @@ impl Report for HandlebarsReport {
println!("The report file has been created. Now it's time to fill it.")
}
- f.write_all(
- self.handler.render("report", results)?.as_bytes(),
- )?;
+ f.write_all(self.handler.render("report", results)?.as_bytes())?;
- for entry in fs::read_dir(config.get_template_path())? {
+ for entry in fs::read_dir(config.template_path())? {
let entry = entry?;
let entry_path = entry.path();
if entry.file_type()?.is_dir() {
copy_folder(
&entry_path,
&config
- .get_results_folder()
- .join(&results.get_app_package())
+ .results_folder()
+ .join(&results.app_package())
.join(entry_path.file_name().unwrap()),
)?;
} else {
@@ -273,7 +274,7 @@ impl Report for HandlebarsReport {
_ => {
let _ = fs::copy(
&entry_path,
- &config.get_results_folder().join(&results.get_app_package()),
+ &config.results_folder().join(&results.app_package()),
)?;
}
}
@@ -285,3 +286,28 @@ impl Report for HandlebarsReport {
Ok(())
}
}
+
+/// Handlebars templates testing module.
+#[cfg(test)]
+mod test {
+ use super::Report;
+ use crate::config::Config;
+
+ /// Test the creation of a new report.
+ #[test]
+ fn it_new() {
+ let _ = Report::new(&Config::default().template_path(), "test").unwrap();
+ }
+
+ /// Test the failure of the creation of an invalid new report.
+ #[test]
+ fn it_new_failure() {
+ assert!(Report::new("random path", "test").is_err());
+ }
+
+ /// Tests handlebars template loading.
+ #[test]
+ fn it_load_templates() {
+ let _ = Report::load_templates(&Config::default().template_path()).unwrap();
+ }
+}
diff --git a/src/results/report/json.rs b/src/results/report/json.rs
index 7dc150a4d..93aad7f91 100644
--- a/src/results/report/json.rs
+++ b/src/results/report/json.rs
@@ -1,29 +1,35 @@
-use results::report::Report;
-use results::Results;
-use config::Config;
-use std::io::BufWriter;
-use std::fs::File;
+//! JSON report generation module.
+
+use std::{fs::File, io::BufWriter};
+
+use failure::Error;
use serde_json::ser;
-use error::*;
+use crate::{
+ config::Config,
+ results::{report::Generator, Results},
+};
+/// JSON report generator.
pub struct Json;
impl Json {
+ /// Creates a new JSON report generator.
pub fn new() -> Self {
Json
}
}
-impl Report for Json {
- fn generate(&mut self, config: &Config, results: &Results) -> Result<()> {
+impl Generator for Json {
+ #[cfg_attr(feature = "cargo-clippy", allow(print_stdout))]
+ fn generate(&mut self, config: &Config, results: &Results) -> Result<(), Error> {
if config.is_verbose() {
println!("Starting JSON report generation. First we create the file.")
}
let mut f = BufWriter::new(File::create(
config
- .get_results_folder()
- .join(&results.get_app_package())
+ .results_folder()
+ .join(&results.app_package())
.join("results.json"),
)?);
if config.is_verbose() {
diff --git a/src/results/report/mod.rs b/src/results/report/mod.rs
index 7a8ec6088..23de52abd 100644
--- a/src/results/report/mod.rs
+++ b/src/results/report/mod.rs
@@ -1,13 +1,15 @@
-mod json;
+//! Report generation module.
+
mod handlebars;
+mod json;
-use results::Results;
-use config::Config;
-use error::*;
+use failure::Error;
-pub use self::json::Json;
-pub use self::handlebars::HandlebarsReport;
+pub use self::{handlebars::Report as HandlebarsReport, json::Json};
+use crate::{config::Config, results::Results};
-pub trait Report {
- fn generate(&mut self, config: &Config, result: &Results) -> Result<()>;
+/// Trait that represents a type that can generate a report.
+pub trait Generator {
+ /// Generates an actual report.
+ fn generate(&mut self, config: &Config, result: &Results) -> Result<(), Error>;
}
diff --git a/src/results/sdk_number.rs b/src/results/sdk_number.rs
new file mode 100644
index 000000000..18cb8f473
--- /dev/null
+++ b/src/results/sdk_number.rs
@@ -0,0 +1,738 @@
+//! Android SDK numbering scheme.
+
+use semver::{Identifier, Version};
+
+/// Android SDK number representation.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum SdkNumber {
+ /// API version 1.
+ Api1,
+ /// API version 2.
+ Api2,
+ /// API version 3.
+ Api3,
+ /// API version 4.
+ Api4,
+ /// API version 5.
+ Api5,
+ /// API version 6.
+ Api6,
+ /// API version 7.
+ Api7,
+ /// API version 8.
+ Api8,
+ /// API version 9.
+ Api9,
+ /// API version 10.
+ Api10,
+ /// API version 11.
+ Api11,
+ /// API version 12.
+ Api12,
+ /// API version 13.
+ Api13,
+ /// API version 14.
+ Api14,
+ /// API version 15.
+ Api15,
+ /// API version 16.
+ Api16,
+ /// API version 17.
+ Api17,
+ /// API version 18.
+ Api18,
+ /// API version 19.
+ Api19,
+ /// API version 20.
+ Api20,
+ /// API version 21.
+ Api21,
+ /// API version 22.
+ Api22,
+ /// API version 23.
+ Api23,
+ /// API version 24.
+ Api24,
+ /// API version 25.
+ Api25,
+ /// API version 26.
+ Api26,
+
+ /// Development API version.
+ Development,
+ /// Unknown API version.
+ Unknown(u32),
+}
+
+/// Main implementation of the SDK numbers.
+///
+/// As per:
+impl SdkNumber {
+ /// Gets the SDK API version number.
+ pub fn number(self) -> u32 {
+ match self {
+ SdkNumber::Api1 => 1,
+ SdkNumber::Api2 => 2,
+ SdkNumber::Api3 => 3,
+ SdkNumber::Api4 => 4,
+ SdkNumber::Api5 => 5,
+ SdkNumber::Api6 => 6,
+ SdkNumber::Api7 => 7,
+ SdkNumber::Api8 => 8,
+ SdkNumber::Api9 => 9,
+ SdkNumber::Api10 => 10,
+ SdkNumber::Api11 => 11,
+ SdkNumber::Api12 => 12,
+ SdkNumber::Api13 => 13,
+ SdkNumber::Api14 => 14,
+ SdkNumber::Api15 => 15,
+ SdkNumber::Api16 => 16,
+ SdkNumber::Api17 => 17,
+ SdkNumber::Api18 => 18,
+ SdkNumber::Api19 => 19,
+ SdkNumber::Api20 => 20,
+ SdkNumber::Api21 => 21,
+ SdkNumber::Api22 => 22,
+ SdkNumber::Api23 => 23,
+ SdkNumber::Api24 => 24,
+ SdkNumber::Api25 => 25,
+ SdkNumber::Api26 => 26,
+
+ SdkNumber::Development => 10_000,
+ SdkNumber::Unknown(v) => v,
+ }
+ }
+
+ /// Gets the Android version number.
+ pub fn version(self) -> Option {
+ match self {
+ SdkNumber::Api1 => Some(Version {
+ major: 1,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api2 => Some(Version {
+ major: 1,
+ minor: 1,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api3 => Some(Version {
+ major: 1,
+ minor: 5,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api4 => Some(Version {
+ major: 1,
+ minor: 6,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api5 => Some(Version {
+ major: 2,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api6 => Some(Version {
+ major: 2,
+ minor: 0,
+ patch: 1,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api7 => Some(Version {
+ major: 2,
+ minor: 1,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api8 => Some(Version {
+ major: 2,
+ minor: 2,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api9 => Some(Version {
+ major: 2,
+ minor: 3,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api10 => Some(Version {
+ major: 2,
+ minor: 3,
+ patch: 3,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api11 => Some(Version {
+ major: 3,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api12 => Some(Version {
+ major: 3,
+ minor: 1,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api13 => Some(Version {
+ major: 3,
+ minor: 2,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api14 => Some(Version {
+ major: 4,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api15 => Some(Version {
+ major: 4,
+ minor: 0,
+ patch: 3,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api16 => Some(Version {
+ major: 4,
+ minor: 1,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api17 => Some(Version {
+ major: 4,
+ minor: 2,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api18 => Some(Version {
+ major: 4,
+ minor: 3,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api19 => Some(Version {
+ major: 4,
+ minor: 4,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api20 => Some(Version {
+ major: 4,
+ minor: 4,
+ patch: 0,
+ pre: vec![],
+ build: vec![Identifier::AlphaNumeric("W".to_owned())],
+ }),
+ SdkNumber::Api21 => Some(Version {
+ major: 5,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api22 => Some(Version {
+ major: 5,
+ minor: 1,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api23 => Some(Version {
+ major: 6,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api24 => Some(Version {
+ major: 7,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api25 => Some(Version {
+ major: 7,
+ minor: 1,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+ SdkNumber::Api26 => Some(Version {
+ major: 8,
+ minor: 0,
+ patch: 0,
+ pre: vec![],
+ build: vec![],
+ }),
+
+ SdkNumber::Development | SdkNumber::Unknown(_) => None,
+ }
+ }
+
+ /// Gets the name of the Android release.
+ pub fn name(&self) -> &str {
+ match self {
+ SdkNumber::Api1 | SdkNumber::Api2 => "Base",
+ SdkNumber::Api3 => "Cupcake",
+ SdkNumber::Api4 => "Donut",
+ SdkNumber::Api5 | SdkNumber::Api6 => "Eclair",
+ SdkNumber::Api7 => "Eclair MR1",
+ SdkNumber::Api8 => "Froyo",
+ SdkNumber::Api9 => "Gingerbread",
+ SdkNumber::Api10 => "Gingerbread MR1",
+ SdkNumber::Api11 => "Honeycomb",
+ SdkNumber::Api12 => "Honeycomb MR1",
+ SdkNumber::Api13 => "Honeycomb MR2",
+ SdkNumber::Api14 => "Ice Cream Sandwich",
+ SdkNumber::Api15 => "Ice Cream Sandwich MR1",
+ SdkNumber::Api16 => "Jelly Bean",
+ SdkNumber::Api17 => "Jelly Bean MR1",
+ SdkNumber::Api18 => "Jelly Bean MR2",
+ SdkNumber::Api19 => "KitKat",
+ SdkNumber::Api20 => "KitKat Watch",
+ SdkNumber::Api21 => "Lollipop",
+ SdkNumber::Api22 => "Lollipop MR1",
+ SdkNumber::Api23 => "Marshmallow",
+ SdkNumber::Api24 => "Nougat",
+ SdkNumber::Api25 => "Nougat MR1",
+ SdkNumber::Api26 => "Oreo",
+
+ SdkNumber::Development => "Development",
+ SdkNumber::Unknown(_) => "Unknown",
+ }
+ }
+}
+
+impl From for SdkNumber {
+ fn from(version: u32) -> Self {
+ match version {
+ 1 => SdkNumber::Api1,
+ 2 => SdkNumber::Api2,
+ 3 => SdkNumber::Api3,
+ 4 => SdkNumber::Api4,
+ 5 => SdkNumber::Api5,
+ 6 => SdkNumber::Api6,
+ 7 => SdkNumber::Api7,
+ 8 => SdkNumber::Api8,
+ 9 => SdkNumber::Api9,
+ 10 => SdkNumber::Api10,
+ 11 => SdkNumber::Api11,
+ 12 => SdkNumber::Api12,
+ 13 => SdkNumber::Api13,
+ 14 => SdkNumber::Api14,
+ 15 => SdkNumber::Api15,
+ 16 => SdkNumber::Api16,
+ 17 => SdkNumber::Api17,
+ 18 => SdkNumber::Api18,
+ 19 => SdkNumber::Api19,
+ 20 => SdkNumber::Api20,
+ 21 => SdkNumber::Api21,
+ 22 => SdkNumber::Api22,
+ 23 => SdkNumber::Api23,
+ 24 => SdkNumber::Api24,
+ 25 => SdkNumber::Api25,
+ 26 => SdkNumber::Api26,
+
+ 10_000 => SdkNumber::Development,
+ t => SdkNumber::Unknown(t),
+ }
+ }
+}
+
+/// Prettifies the android version number so that it's shown as the official version.
+pub fn prettify_android_version(version: &Version) -> String {
+ format!(
+ "{}.{}{}{}",
+ version.major,
+ version.minor,
+ if version.patch == 0 {
+ String::new()
+ } else {
+ format!(".{}", version.patch)
+ },
+ if let Some(b) = version.build.get(0) {
+ format!("{}", b)
+ } else {
+ String::new()
+ }
+ )
+}
+
+#[cfg(test)]
+#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
+mod tests {
+ use super::{prettify_android_version, Identifier, SdkNumber, Version};
+
+ /// Checks the correctness of the `SdkNumber` when transforming a `u32`.
+ #[test]
+ fn it_from_u32() {
+ assert_eq!(SdkNumber::from(1), SdkNumber::Api1);
+ assert_eq!(SdkNumber::from(2), SdkNumber::Api2);
+ assert_eq!(SdkNumber::from(3), SdkNumber::Api3);
+ assert_eq!(SdkNumber::from(4), SdkNumber::Api4);
+ assert_eq!(SdkNumber::from(5), SdkNumber::Api5);
+ assert_eq!(SdkNumber::from(6), SdkNumber::Api6);
+ assert_eq!(SdkNumber::from(7), SdkNumber::Api7);
+ assert_eq!(SdkNumber::from(8), SdkNumber::Api8);
+ assert_eq!(SdkNumber::from(9), SdkNumber::Api9);
+ assert_eq!(SdkNumber::from(10), SdkNumber::Api10);
+ assert_eq!(SdkNumber::from(11), SdkNumber::Api11);
+ assert_eq!(SdkNumber::from(12), SdkNumber::Api12);
+ assert_eq!(SdkNumber::from(13), SdkNumber::Api13);
+ assert_eq!(SdkNumber::from(14), SdkNumber::Api14);
+ assert_eq!(SdkNumber::from(15), SdkNumber::Api15);
+ assert_eq!(SdkNumber::from(16), SdkNumber::Api16);
+ assert_eq!(SdkNumber::from(17), SdkNumber::Api17);
+ assert_eq!(SdkNumber::from(18), SdkNumber::Api18);
+ assert_eq!(SdkNumber::from(19), SdkNumber::Api19);
+ assert_eq!(SdkNumber::from(20), SdkNumber::Api20);
+ assert_eq!(SdkNumber::from(21), SdkNumber::Api21);
+ assert_eq!(SdkNumber::from(22), SdkNumber::Api22);
+ assert_eq!(SdkNumber::from(23), SdkNumber::Api23);
+ assert_eq!(SdkNumber::from(24), SdkNumber::Api24);
+ assert_eq!(SdkNumber::from(25), SdkNumber::Api25);
+ assert_eq!(SdkNumber::from(26), SdkNumber::Api26);
+
+ // Unknown APIs.
+ assert_eq!(SdkNumber::from(27), SdkNumber::Unknown(27));
+ assert_eq!(SdkNumber::from(77), SdkNumber::Unknown(77));
+ assert_eq!(SdkNumber::from(2345), SdkNumber::Unknown(2345));
+
+ // Development API.
+ assert_eq!(SdkNumber::from(10_000), SdkNumber::Development);
+ }
+
+ /// Checks the correctnes back from the SDK to its number.
+ #[test]
+ fn it_get_number() {
+ assert_eq!(SdkNumber::Api1.number(), 1);
+ assert_eq!(SdkNumber::Api2.number(), 2);
+ assert_eq!(SdkNumber::Api3.number(), 3);
+ assert_eq!(SdkNumber::Api4.number(), 4);
+ assert_eq!(SdkNumber::Api5.number(), 5);
+ assert_eq!(SdkNumber::Api6.number(), 6);
+ assert_eq!(SdkNumber::Api7.number(), 7);
+ assert_eq!(SdkNumber::Api8.number(), 8);
+ assert_eq!(SdkNumber::Api9.number(), 9);
+ assert_eq!(SdkNumber::Api10.number(), 10);
+ assert_eq!(SdkNumber::Api11.number(), 11);
+ assert_eq!(SdkNumber::Api12.number(), 12);
+ assert_eq!(SdkNumber::Api13.number(), 13);
+ assert_eq!(SdkNumber::Api14.number(), 14);
+ assert_eq!(SdkNumber::Api15.number(), 15);
+ assert_eq!(SdkNumber::Api16.number(), 16);
+ assert_eq!(SdkNumber::Api17.number(), 17);
+ assert_eq!(SdkNumber::Api18.number(), 18);
+ assert_eq!(SdkNumber::Api19.number(), 19);
+ assert_eq!(SdkNumber::Api20.number(), 20);
+ assert_eq!(SdkNumber::Api21.number(), 21);
+ assert_eq!(SdkNumber::Api22.number(), 22);
+ assert_eq!(SdkNumber::Api23.number(), 23);
+ assert_eq!(SdkNumber::Api24.number(), 24);
+ assert_eq!(SdkNumber::Api25.number(), 25);
+ assert_eq!(SdkNumber::Api26.number(), 26);
+
+ // Unknown APIs.
+ assert_eq!(SdkNumber::Unknown(27).number(), 27);
+ assert_eq!(SdkNumber::Unknown(133).number(), 133);
+ assert_eq!(SdkNumber::Unknown(4392).number(), 4392);
+
+ // Development API.
+ assert_eq!(SdkNumber::Development.number(), 10_000);
+ }
+
+ /// Checks that the Android version number is correct for each API.
+ #[test]
+ fn it_get_version() {
+ assert_eq!(
+ SdkNumber::Api1.version().unwrap(),
+ Version::parse("1.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api2.version().unwrap(),
+ Version::parse("1.1.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api3.version().unwrap(),
+ Version::parse("1.5.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api4.version().unwrap(),
+ Version::parse("1.6.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api5.version().unwrap(),
+ Version::parse("2.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api6.version().unwrap(),
+ Version::parse("2.0.1").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api7.version().unwrap(),
+ Version::parse("2.1.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api8.version().unwrap(),
+ Version::parse("2.2.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api9.version().unwrap(),
+ Version::parse("2.3.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api10.version().unwrap(),
+ Version::parse("2.3.3").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api11.version().unwrap(),
+ Version::parse("3.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api12.version().unwrap(),
+ Version::parse("3.1.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api13.version().unwrap(),
+ Version::parse("3.2.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api14.version().unwrap(),
+ Version::parse("4.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api15.version().unwrap(),
+ Version::parse("4.0.3").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api16.version().unwrap(),
+ Version::parse("4.1.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api17.version().unwrap(),
+ Version::parse("4.2.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api18.version().unwrap(),
+ Version::parse("4.3.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api19.version().unwrap(),
+ Version::parse("4.4.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api20.version().unwrap(),
+ Version {
+ major: 4,
+ minor: 4,
+ patch: 0,
+ pre: vec![],
+ build: vec![Identifier::AlphaNumeric("W".to_owned())],
+ }
+ );
+ assert_eq!(
+ SdkNumber::Api21.version().unwrap(),
+ Version::parse("5.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api22.version().unwrap(),
+ Version::parse("5.1.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api23.version().unwrap(),
+ Version::parse("6.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api24.version().unwrap(),
+ Version::parse("7.0.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api25.version().unwrap(),
+ Version::parse("7.1.0").unwrap()
+ );
+ assert_eq!(
+ SdkNumber::Api26.version().unwrap(),
+ Version::parse("8.0.0").unwrap()
+ );
+
+ // Unknown APIs.
+ assert!(SdkNumber::Unknown(27).version().is_none());
+ assert!(SdkNumber::Unknown(201).version().is_none());
+ assert!(SdkNumber::Unknown(5602).version().is_none());
+
+ // Development API.
+ assert!(SdkNumber::Development.version().is_none());
+ }
+
+ /// Checks that the names associated with API versions are correct.
+ #[test]
+ fn it_get_name() {
+ assert_eq!(SdkNumber::Api1.name(), "Base");
+ assert_eq!(SdkNumber::Api2.name(), "Base");
+ assert_eq!(SdkNumber::Api3.name(), "Cupcake");
+ assert_eq!(SdkNumber::Api4.name(), "Donut");
+ assert_eq!(SdkNumber::Api5.name(), "Eclair");
+ assert_eq!(SdkNumber::Api6.name(), "Eclair");
+ assert_eq!(SdkNumber::Api7.name(), "Eclair MR1");
+ assert_eq!(SdkNumber::Api8.name(), "Froyo");
+ assert_eq!(SdkNumber::Api9.name(), "Gingerbread");
+ assert_eq!(SdkNumber::Api10.name(), "Gingerbread MR1");
+ assert_eq!(SdkNumber::Api11.name(), "Honeycomb");
+ assert_eq!(SdkNumber::Api12.name(), "Honeycomb MR1");
+ assert_eq!(SdkNumber::Api13.name(), "Honeycomb MR2");
+ assert_eq!(SdkNumber::Api14.name(), "Ice Cream Sandwich");
+ assert_eq!(SdkNumber::Api15.name(), "Ice Cream Sandwich MR1");
+ assert_eq!(SdkNumber::Api16.name(), "Jelly Bean");
+ assert_eq!(SdkNumber::Api17.name(), "Jelly Bean MR1");
+ assert_eq!(SdkNumber::Api18.name(), "Jelly Bean MR2");
+ assert_eq!(SdkNumber::Api19.name(), "KitKat");
+ assert_eq!(SdkNumber::Api20.name(), "KitKat Watch");
+ assert_eq!(SdkNumber::Api21.name(), "Lollipop");
+ assert_eq!(SdkNumber::Api22.name(), "Lollipop MR1");
+ assert_eq!(SdkNumber::Api23.name(), "Marshmallow");
+ assert_eq!(SdkNumber::Api24.name(), "Nougat");
+ assert_eq!(SdkNumber::Api25.name(), "Nougat MR1");
+ assert_eq!(SdkNumber::Api26.name(), "Oreo");
+
+ // Unknown APIs.
+ assert_eq!(SdkNumber::Unknown(27).name(), "Unknown");
+ assert_eq!(SdkNumber::Unknown(302).name(), "Unknown");
+ assert_eq!(SdkNumber::Unknown(7302).name(), "Unknown");
+
+ // Development API.
+ assert_eq!(SdkNumber::Development.name(), "Development");
+ }
+
+ /// Checks that Android versions are properly printed.
+ #[test]
+ fn it_prettify_android_version() {
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api1.version().unwrap()),
+ "1.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api2.version().unwrap()),
+ "1.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api3.version().unwrap()),
+ "1.5"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api4.version().unwrap()),
+ "1.6"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api5.version().unwrap()),
+ "2.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api6.version().unwrap()),
+ "2.0.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api7.version().unwrap()),
+ "2.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api8.version().unwrap()),
+ "2.2"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api9.version().unwrap()),
+ "2.3"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api10.version().unwrap()),
+ "2.3.3"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api11.version().unwrap()),
+ "3.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api12.version().unwrap()),
+ "3.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api13.version().unwrap()),
+ "3.2"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api14.version().unwrap()),
+ "4.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api15.version().unwrap()),
+ "4.0.3"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api16.version().unwrap()),
+ "4.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api17.version().unwrap()),
+ "4.2"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api18.version().unwrap()),
+ "4.3"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api19.version().unwrap()),
+ "4.4"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api20.version().unwrap()),
+ "4.4W"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api21.version().unwrap()),
+ "5.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api22.version().unwrap()),
+ "5.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api23.version().unwrap()),
+ "6.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api24.version().unwrap()),
+ "7.0"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api25.version().unwrap()),
+ "7.1"
+ );
+ assert_eq!(
+ prettify_android_version(&SdkNumber::Api26.version().unwrap()),
+ "8.0"
+ );
+ }
+}
diff --git a/src/results/utils.rs b/src/results/utils.rs
index 9a8835f5b..12a75b9a8 100644
--- a/src/results/utils.rs
+++ b/src/results/utils.rs
@@ -3,20 +3,22 @@
//! In this module you can find structures like `Vulnerability` and `Fingerprint` that contain the
//! information for results.
-use std::result::Result as StdResult;
-use std::fs::File;
-use std::io::Read;
-use std::cmp::Ordering;
-use std::path::{Path, PathBuf};
-use std::borrow::Cow;
-
+use std::{
+ borrow::Cow,
+ cmp::Ordering,
+ fs::File,
+ io::Read,
+ path::{Path, PathBuf},
+};
+
+use failure::Error;
+use hex::ToHex;
+use lazy_static::lazy_static;
+use regex::Regex;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use {md5, sha1, sha2};
-use rustc_serialize::hex::ToHex;
-use regex::Regex;
-use error::*;
-use Criticality;
+use crate::criticality::Criticality;
/// Structure to store information about a vulnerability.
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
@@ -47,17 +49,17 @@ impl Vulnerability {
start_line: Option,
end_line: Option,
code: Option,
- ) -> Vulnerability {
- Vulnerability {
- criticality: criticality,
+ ) -> Self {
+ Self {
+ criticality,
name: name.into(),
description: description.into(),
file: match file {
Some(p) => Some(p.as_ref().to_path_buf()),
None => None,
},
- start_line: start_line,
- end_line: end_line,
+ start_line,
+ end_line,
code: match code {
Some(c) => Some(c.into()),
None => None,
@@ -72,7 +74,7 @@ impl Vulnerability {
}
impl Serialize for Vulnerability {
- fn serialize(&self, serializer: S) -> StdResult
+ fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
{
@@ -90,15 +92,13 @@ impl Serialize for Vulnerability {
)?;
ser_struct.serialize_field("criticality", &self.criticality)?;
ser_struct.serialize_field("name", self.name.as_str())?;
- ser_struct.serialize_field(
- "description",
- self.description.as_str(),
- )?;
+ ser_struct.serialize_field("description", self.description.as_str())?;
ser_struct.serialize_field("file", &self.file)?;
if self.code.is_some() {
ser_struct.serialize_field(
"language",
- &self.file
+ &self
+ .file
.as_ref()
.unwrap()
.extension()
@@ -106,19 +106,10 @@ impl Serialize for Vulnerability {
.to_string_lossy(),
)?;
if self.start_line == self.end_line {
- ser_struct.serialize_field(
- "line",
- &(self.start_line.unwrap() + 1),
- )?;
+ ser_struct.serialize_field("line", &(self.start_line.unwrap() + 1))?;
} else {
- ser_struct.serialize_field(
- "start_line",
- &(self.start_line.unwrap() + 1),
- )?;
- ser_struct.serialize_field(
- "end_line",
- &(self.end_line.unwrap() + 1),
- )?;
+ ser_struct.serialize_field("start_line", &(self.start_line.unwrap() + 1))?;
+ ser_struct.serialize_field("end_line", &(self.end_line.unwrap() + 1))?;
}
ser_struct.serialize_field("code", &self.code)?;
}
@@ -127,7 +118,7 @@ impl Serialize for Vulnerability {
}
impl PartialOrd for Vulnerability {
- fn partial_cmp(&self, other: &Vulnerability) -> Option {
+ fn partial_cmp(&self, other: &Self) -> Option {
Some(
(
&self.criticality,
@@ -135,7 +126,8 @@ impl PartialOrd for Vulnerability {
&self.start_line,
&self.end_line,
&self.name,
- ).cmp(&(
+ )
+ .cmp(&(
&other.criticality,
&other.file,
&other.start_line,
@@ -160,7 +152,7 @@ impl FingerPrint {
/// Creates a new fingerprint.
///
/// This function will read the complete file and generate its MD5, SHA-1 and SHA-256 hashes.
- pub fn new>(package: P) -> Result {
+ pub fn new>(package: P) -> Result {
use sha2::Digest;
let mut f = File::open(package)?;
@@ -175,7 +167,7 @@ impl FingerPrint {
let mut sha256_res = [0_u8; 32];
sha256_res.clone_from_slice(&sha256.result()[..]);
- Ok(FingerPrint {
+ Ok(Self {
md5: md5::compute(&buffer),
sha1: sha1.digest(),
sha256: sha256_res,
@@ -184,17 +176,20 @@ impl FingerPrint {
}
impl Serialize for FingerPrint {
- fn serialize(&self, serializer: S) -> StdResult
+ fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
{
let mut ser_struct = serializer.serialize_struct("fingerprint", 3)?;
- ser_struct.serialize_field(
- "md5",
- &format!("{:x}", self.md5),
- )?;
+ ser_struct.serialize_field("md5", &format!("{:x}", self.md5))?;
ser_struct.serialize_field("sha1", &self.sha1.to_string())?;
- ser_struct.serialize_field("sha256", &self.sha256.to_hex())?;
+ let mut sha256_hex = String::new();
+ // It should never fail, we are writing directly to memory, without I/O access
+ // That's why the `expect()` should never panic.
+ self.sha256
+ .write_hex(&mut sha256_hex)
+ .expect("the SHA-256 fingerprinting of the application failed");
+ ser_struct.serialize_field("sha256", &sha256_hex)?;
ser_struct.end()
}
}
diff --git a/src/static_analysis/certificate.rs b/src/static_analysis/certificate.rs
index 6d8efd817..b87956e92 100644
--- a/src/static_analysis/certificate.rs
+++ b/src/static_analysis/certificate.rs
@@ -1,16 +1,17 @@
//! This module performs the static analysis of the certificate of the application.
-use std::fs;
-use std::process::Command;
-use std::borrow::Borrow;
-use std::error::Error as StdError;
+use std::{borrow::Borrow, fs, process::Command};
+use chrono::{Datelike, Local};
use colored::Colorize;
-use chrono::{Local, Datelike};
+use failure::{bail, Error, ResultExt};
-use {Config, Criticality, Result, print_vulnerability, print_warning};
-use results::{Results, Vulnerability};
-use error::*;
+use crate::{
+ criticality::Criticality,
+ print_vulnerability, print_warning,
+ results::{Results, Vulnerability},
+ Config,
+};
/// Parses the given month string.
///
@@ -40,14 +41,14 @@ pub fn certificate_analysis>(
config: &Config,
package: S,
results: &mut Results,
-) -> Result<()> {
+) -> Result<(), Error> {
if config.is_verbose() {
println!("Reading and analyzing the certificates…")
}
// Gets the path to the certificate files.
let path = config
- .get_dist_folder()
+ .dist_folder()
.join(package.as_ref())
.join("original")
.join("META-INF");
@@ -58,11 +59,10 @@ pub fn certificate_analysis>(
Ok(f) => f,
Err(e) => {
print_warning(format!(
- "An error occurred when reading the \
- {} dir searching certificates. \
- Certificate analysis will be skipped. More info: {}",
+ "An error occurred when reading the {} dir searching certificates. Certificate \
+ analysis will be skipped. More info: {}",
path.display(),
- e.description()
+ e
));
break;
}
@@ -85,7 +85,8 @@ pub fn certificate_analysis>(
// We found a certificate, let's get its information.
if is_cert {
- let output = Command::new("openssl").arg("pkcs7")
+ let output = Command::new("openssl")
+ .arg("pkcs7")
.arg("-inform")
.arg("DER")
.arg("-in")
@@ -94,16 +95,12 @@ pub fn certificate_analysis>(
.arg("-print_certs")
.arg("-text")
.output()
- .chain_err(|| {
- "There was an error when executing the openssl command to check the certificate"
- })?;
+ .context("there was an error when executing the openssl command to check the certificate")?;
if !output.status.success() {
- return Err(
- format!(
- "The openssl command returned an error. More info: {}",
- String::from_utf8_lossy(&output.stderr[..])
- ).into(),
+ bail!(
+ "the openssl command returned an error. More info: {}",
+ String::from_utf8_lossy(&output.stderr[..])
);
};
@@ -169,13 +166,15 @@ pub fn certificate_analysis>(
let after = after.nth(1).unwrap();
let cert_year = after[16..20].parse::().unwrap();
let cert_month = parse_month(&after[0..3]);
- let cert_day = match after[4..6].parse::() { //if day<10 parse 1 number
+ let cert_day = match after[4..6].parse::() {
+ //if day<10 parse 1 number
Ok(n) => n,
Err(_) => after[5..6].parse::().unwrap(),
};
- if year > cert_year || (year == cert_year && month > cert_month) ||
- (year == cert_year && month == cert_month && day > cert_day)
+ if year > cert_year
+ || (year == cert_year && month > cert_month)
+ || (year == cert_year && month == cert_month && day > cert_day)
{
let criticality = Criticality::High;
let description = "The certificate of the application has expired. You should not \
diff --git a/src/static_analysis/code.rs b/src/static_analysis/code.rs
index 97781c1ac..b9a111b0e 100644
--- a/src/static_analysis/code.rs
+++ b/src/static_analysis/code.rs
@@ -1,24 +1,30 @@
-use std::fs;
-use std::fs::{File, DirEntry};
-use std::io::Read;
-use std::str::FromStr;
-use std::path::Path;
-use std::borrow::Borrow;
-use std::thread;
-use std::sync::{Arc, Mutex};
-use std::slice::Iter;
-use std::error::Error as StdError;
+//! Code analysis module.
+
+use std::{
+ borrow::Borrow,
+ fmt,
+ fs::{self, DirEntry, File},
+ path::Path,
+ slice::Iter,
+ sync::{Arc, Mutex},
+ thread,
+};
-use serde_json;
-use serde_json::value::Value;
-use regex::Regex;
use colored::Colorize;
+use failure::{Error, Fail, ResultExt};
+use regex::Regex;
+use serde::de::{self, Deserializer, SeqAccess, Visitor};
+use serde_json;
-use {Config, Criticality, print_warning, print_vulnerability, get_code};
-use results::{Results, Vulnerability};
-use super::manifest::{Permission, Manifest};
-use error::*;
+use super::manifest::{Manifest, Permission};
+use crate::{
+ criticality::Criticality,
+ error, get_code, print_vulnerability, print_warning,
+ results::{Results, Vulnerability},
+ Config,
+};
+/// Analyzes the whole codebase of the application.
pub fn analysis>(
manifest: Option,
config: &Config,
@@ -30,7 +36,7 @@ pub fn analysis>(
Err(e) => {
print_warning(format!(
"An error occurred when loading code analysis rules. Error: {}",
- e.description()
+ e
));
return;
}
@@ -39,9 +45,9 @@ pub fn analysis>(
let mut files: Vec = Vec::new();
if let Err(e) = add_files_to_vec("", &mut files, package.as_ref(), config) {
print_warning(format!(
- "An error occurred when reading files for analysis, the results \
- might be incomplete. Error: {}",
- e.description()
+ "An error occurred when reading files for analysis, the results might be incomplete. \
+ Error: {}",
+ e
));
}
let total_files = files.len();
@@ -50,23 +56,23 @@ pub fn analysis>(
let manifest = Arc::new(manifest);
let found_vulns: Arc>> = Arc::new(Mutex::new(Vec::new()));
let files = Arc::new(Mutex::new(files));
- let dist_folder = Arc::new(config.get_dist_folder().join(package.as_ref()));
+ let dist_folder = Arc::new(config.dist_folder().join(package.as_ref()));
if config.is_verbose() {
println!(
"Starting analysis of the code with {} threads. {} files to go!",
- format!("{}", config.get_threads()).bold(),
+ format!("{}", config.threads()).bold(),
format!("{}", total_files).bold()
);
}
- let handles: Vec<_> = (0..config.get_threads())
+ let handles: Vec<_> = (0..config.threads())
.map(|_| {
- let thread_manifest = manifest.clone();
- let thread_files = files.clone();
- let thread_rules = rules.clone();
- let thread_vulns = found_vulns.clone();
- let thread_dist_folder = dist_folder.clone();
+ let thread_manifest = Arc::clone(&manifest);
+ let thread_files = Arc::clone(&files);
+ let thread_rules = Arc::clone(&rules);
+ let thread_vulns = Arc::clone(&found_vulns);
+ let thread_dist_folder = Arc::clone(&dist_folder);
thread::spawn(move || loop {
let f = {
@@ -81,21 +87,19 @@ pub fn analysis>(
&thread_rules,
&thread_manifest,
&thread_vulns,
- )
- {
+ ) {
print_warning(format!(
- "Error analyzing file {}. The analysis will \
- continue, though. Error: {}",
+ "could not analyze `{}`. The analysis will continue, though. \
+ Error: {}",
f.path().display(),
- e.description()
+ e
))
}
}
None => break,
}
})
- })
- .collect();
+ }).collect();
if config.is_verbose() {
let mut last_print = 0;
@@ -105,7 +109,6 @@ pub fn analysis>(
Err(_) => 1,
} > 0
{
-
let left = match files.lock() {
Ok(f) => f.len(),
Err(_) => continue,
@@ -120,9 +123,9 @@ pub fn analysis>(
for t in handles {
if let Err(e) = t.join() {
- #[allow(use_debug)]
+ #[cfg_attr(feature = "cargo-clippy", allow(use_debug))]
print_warning(format!(
- "An error occurred when joining analysis threads: Error: {:?}",
+ "an error occurred when joining analysis threads: Error: {:?}",
e
));
}
@@ -140,20 +143,20 @@ pub fn analysis>(
}
}
+/// Analyzes the given file.
fn analyze_file, T: AsRef>(
path: P,
dist_folder: T,
rules: &[Rule],
manifest: &Option,
results: &Mutex>,
-) -> Result<()> {
- let mut f = File::open(&path)?;
- let mut code = String::new();
- let _ = f.read_to_string(&mut code)?;
+) -> Result<(), Error> {
+ let code = fs::read_to_string(&path)?;
'check: for rule in rules {
- if manifest.is_some() && rule.get_max_sdk().is_some() &&
- rule.get_max_sdk().unwrap() < manifest.as_ref().unwrap().get_min_sdk()
+ if manifest.is_some()
+ && rule.max_sdk().is_some()
+ && rule.max_sdk().unwrap() < manifest.as_ref().unwrap().min_sdk()
{
continue 'check;
}
@@ -166,45 +169,42 @@ fn analyze_file, T: AsRef>(
}
}
- for permission in rule.get_permissions() {
- if manifest.is_none() ||
- !manifest
- .as_ref()
- .unwrap()
- .get_permission_checklist()
- .needs_permission(*permission)
+ for permission in rule.permissions() {
+ if manifest.is_none() || !manifest
+ .as_ref()
+ .unwrap()
+ .permission_checklist()
+ .needs_permission(*permission)
{
continue 'check;
}
}
- 'rule: for m in rule.get_regex().find_iter(code.as_str()) {
- for white in rule.get_whitelist() {
+ 'rule: for m in rule.regex().find_iter(code.as_str()) {
+ for white in rule.whitelist() {
if white.is_match(&code[m.start()..m.end()]) {
continue 'rule;
}
}
- match rule.get_forward_check() {
+ match rule.forward_check() {
None => {
let start_line = get_line_for(m.start(), code.as_str());
let end_line = get_line_for(m.end(), code.as_str());
let mut results = results.lock().unwrap();
results.push(Vulnerability::new(
- rule.get_criticality(),
- rule.get_label(),
- rule.get_description(),
+ rule.criticality(),
+ rule.label(),
+ rule.description(),
Some(path.as_ref().strip_prefix(&dist_folder).unwrap()),
Some(start_line),
Some(end_line),
Some(get_code(code.as_str(), start_line, end_line)),
));
- print_vulnerability(rule.get_description(), rule.get_criticality());
+ print_vulnerability(rule.description(), rule.criticality());
}
Some(check) => {
- let caps = rule.get_regex()
- .captures(&code[m.start()..m.end()])
- .unwrap();
+ let caps = rule.regex().captures(&code[m.start()..m.end()]).unwrap();
let fcheck1 = caps.name("fc1");
let fcheck2 = caps.name("fc2");
@@ -222,11 +222,10 @@ fn analyze_file, T: AsRef>(
Ok(r) => r,
Err(e) => {
print_warning(format!(
- "There was an error creating the \
- forward_check '{}'. The rule will be \
- skipped. {}",
+ "there was an error creating the forward_check '{}'. The rule will \
+ be skipped. {}",
r,
- e.description()
+ e
));
break 'rule;
}
@@ -237,16 +236,16 @@ fn analyze_file, T: AsRef>(
let end_line = get_line_for(m.end(), code.as_str());
let mut results = results.lock().unwrap();
results.push(Vulnerability::new(
- rule.get_criticality(),
- rule.get_label(),
- rule.get_description(),
+ rule.criticality(),
+ rule.label(),
+ rule.description(),
Some(path.as_ref().strip_prefix(&dist_folder).unwrap()),
Some(start_line),
Some(end_line),
Some(get_code(code.as_str(), start_line, end_line)),
));
- print_vulnerability(rule.get_description(), rule.get_criticality());
+ print_vulnerability(rule.description(), rule.criticality());
}
}
}
@@ -274,22 +273,22 @@ fn add_files_to_vec, S: AsRef>(
vec: &mut Vec,
package: S,
config: &Config,
-) -> Result<()> {
- if path.as_ref() == Path::new("classes/android") ||
- path.as_ref() == Path::new("classes/com/google/android/gms") ||
- path.as_ref() == Path::new("smali")
+) -> Result<(), Error> {
+ if path.as_ref() == Path::new("classes/android")
+ || path.as_ref() == Path::new("classes/com/google/android/gms")
+ || path.as_ref() == Path::new("smali")
{
return Ok(());
}
- let real_path = config.get_dist_folder().join(package.as_ref()).join(path);
+ let real_path = config.dist_folder().join(package.as_ref()).join(path);
for f in fs::read_dir(&real_path)? {
let f = match f {
Ok(f) => f,
Err(e) => {
print_warning(format!(
- "There was an error reading the directory {}: {}",
+ "there was an error reading the directory {}: {}",
real_path.display(),
- e.description()
+ e
));
return Err(e.into());
}
@@ -300,7 +299,7 @@ fn add_files_to_vec, S: AsRef>(
if f_type.is_dir() && f_path != real_path.join("original") {
add_files_to_vec(
f.path()
- .strip_prefix(&config.get_dist_folder().join(package.as_ref()))
+ .strip_prefix(&config.dist_folder().join(package.as_ref()))
.unwrap(),
vec,
package.as_ref(),
@@ -308,8 +307,9 @@ fn add_files_to_vec, S: AsRef>(
)?;
} else if f_ext.is_some() {
let filename = f_path.file_name().unwrap().to_string_lossy();
- if filename != "AndroidManifest.xml" && filename != "R.java" &&
- !filename.starts_with("R$")
+ if filename != "AndroidManifest.xml"
+ && filename != "R.java"
+ && !filename.starts_with("R$")
{
match f_ext.unwrap().to_string_lossy().borrow() {
"xml" | "java" => vec.push(f),
@@ -321,49 +321,67 @@ fn add_files_to_vec, S: AsRef>(
Ok(())
}
+/// Vulnerability searching rule.
+#[derive(Debug, Deserialize)]
struct Rule {
+ #[serde(deserialize_with = "deserialize_main_regex")]
regex: Regex,
- permissions: Vec,
+ #[serde(default)]
+ permissions: Box<[Permission]>,
forward_check: Option,
max_sdk: Option,
- whitelist: Vec,
+ #[serde(deserialize_with = "deserialize_whitelist_regex")]
+ #[serde(default)]
+ whitelist: Box<[Regex]>,
label: String,
description: String,
criticality: Criticality,
+ #[serde(deserialize_with = "deserialize_file_regex")]
+ #[serde(default)]
include_file_regex: Option,
+ #[serde(deserialize_with = "deserialize_file_regex")]
+ #[serde(default)]
exclude_file_regex: Option,
}
impl Rule {
- pub fn get_regex(&self) -> &Regex {
+ /// Gets the regex of the rule.
+ pub fn regex(&self) -> &Regex {
&self.regex
}
- pub fn get_permissions(&self) -> Iter {
+ /// Gets the permissions required for this rule to be checked.
+ pub fn permissions(&self) -> Iter {
self.permissions.iter()
}
- pub fn get_forward_check(&self) -> Option<&String> {
+ /// Gets the potential forward check of the rule.
+ pub fn forward_check(&self) -> Option<&String> {
self.forward_check.as_ref()
}
- pub fn get_max_sdk(&self) -> Option {
+ /// Gets the maximum SDK affected by this vulnerability.
+ pub fn max_sdk(&self) -> Option {
self.max_sdk
}
- pub fn get_label(&self) -> &str {
+ /// Gets the label of the vulnerability.
+ pub fn label(&self) -> &str {
self.label.as_str()
}
- pub fn get_description(&self) -> &str {
+ /// Gets the description of the vulnerability.
+ pub fn description(&self) -> &str {
self.description.as_str()
}
- pub fn get_criticality(&self) -> Criticality {
+ /// Gets the criticality for the vulnerabilities found by the rule.
+ pub fn criticality(&self) -> Criticality {
self.criticality
}
- pub fn get_whitelist(&self) -> Iter {
+ /// Gets the whitelist regex list.
+ pub fn whitelist(&self) -> Iter {
self.whitelist.iter()
}
@@ -387,282 +405,225 @@ impl Rule {
}
}
-fn load_rules(config: &Config) -> Result> {
- let f = File::open(config.get_rules_json())?;
- let rules_json: Value = serde_json::from_reader(f)?;
+/// Regular expression serde visitor.
+struct RegexVisitor;
- let mut rules = Vec::new();
- let rules_json = if let Some(a) = rules_json.as_array() {
- a
- } else {
- print_warning("Rules must be a JSON array.");
- return Err(ErrorKind::Parse.into());
- };
+impl<'de> Visitor<'de> for RegexVisitor {
+ type Value = Regex;
- for rule in rules_json {
- let format_warning = format!(
- "Rules must be objects with the following structure:\n{}\nAn optional {} \
- attribute can be added: an array of regular expressions that if matched, \
- the found match will be discarded. You can also include an optional {} \
- attribute: an array of the permissions needed for this rule to be checked. \
- And finally, an optional {} attribute can be added where you can specify a \
- second regular expression to check if the one in the {} attribute matches. \
- You can add one or two capture groups with name from the match to this \
- check, with names {} and {}. To use them you have to include {} or {} in \
- the forward check.",
- "{\n\t\"label\": \"Label for the rule\",\n\t\"description\": \"Long \
- description for this rule\"\n\t\"criticality\": \
- \"warning|low|medium|high|critical\"\n\t\"regex\": \
- \"regex_to_find_vulnerability\"\n}"
- .italic(),
- "whitelist".italic(),
- "permissions".italic(),
- "forward_check".italic(),
- "regex".italic(),
- "fc1".italic(),
- "fc2".italic(),
- "{fc1}".italic(),
- "{fc2}".italic()
- );
- let rule = if let Some(o) = rule.as_object() {
- o
- } else {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
- };
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a valid regular expression")
+ }
+
+ fn visit_str(self, value: &str) -> Result
+ where
+ E: de::Error,
+ {
+ Regex::new(value).map_err(E::custom)
+ }
- if rule.len() < 4 || rule.len() > 8 {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
+ fn visit_borrowed_str(self, value: &'de str) -> Result
+ where
+ E: de::Error,
+ {
+ self.visit_str(value)
+ }
+
+ fn visit_string(self, value: String) -> Result
+ where
+ E: de::Error,
+ {
+ self.visit_str(&value)
+ }
+}
+
+/// Deserializes the main regular expression of a rule.
+fn deserialize_main_regex<'de, D>(deserializer: D) -> Result
+where
+ D: Deserializer<'de>,
+{
+ deserializer.deserialize_str(RegexVisitor)
+}
+
+/// Deserializes the list of whitelist regular expressions.
+fn deserialize_whitelist_regex<'de, D>(deserializer: D) -> Result, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ /// Visitor that deserializes a sequence of regular expressions.
+ struct RegexSeqVisitor;
+
+ impl<'de> Visitor<'de> for RegexSeqVisitor {
+ type Value = Box<[Regex]>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a list of valid regular expressions")
}
- let regex = if let Some(&Value::String(ref r)) = rule.get("regex") {
- match Regex::new(r) {
- Ok(r) => r,
- Err(e) => {
- print_warning(format!(
- "An error occurred when compiling the regular \
- expresion: {}",
- e.description()
- ));
- return Err(ErrorKind::Parse.into());
- }
- }
- } else {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
- };
+ fn visit_seq(self, mut seq: A) -> Result
+ where
+ A: SeqAccess<'de>,
+ {
+ use serde::de::Error as SerdeError;
- let max_sdk = match rule.get("max_sdk") {
- Some(&Value::Number(ref sdk)) if sdk.is_u64() => Some(sdk.as_u64().unwrap() as u32),
- None => None,
- _ => {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
- }
- };
+ let mut list = Vec::with_capacity(seq.size_hint().unwrap_or(0));
- let permissions = match rule.get("permissions") {
- Some(&Value::Array(ref v)) => {
- let mut list = Vec::with_capacity(v.len());
- for p in v {
- list.push(if let Value::String(ref p) = *p {
- if let Ok(p) = Permission::from_str(p) {
- p
- } else {
- print_warning(format!("the permission {} is unknown", p.italic()));
- return Err(ErrorKind::Parse.into());
- }
- } else {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
- });
- }
- list
- }
- Some(_) => {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
+ // While there are entries remaining in the input, add them into our vector.
+ while let Some(regex_str) = seq.next_element::()? {
+ list.push(Regex::new(regex_str.as_str()).map_err(A::Error::custom)?)
}
- None => Vec::with_capacity(0),
- };
- let forward_check = match rule.get("forward_check") {
- Some(&Value::String(ref s)) => {
- let capture_names = regex.capture_names();
- for cap in capture_names {
- match cap {
- Some("fc1") => {
- if !s.contains("{fc1}") {
- print_warning(
- "You must provide the '{fc1}' string where you \
- want the 'fc1' capture to be inserted in the \
- forward check.",
- );
- return Err(ErrorKind::Parse.into());
- }
- }
- Some("fc2") => {
- if !s.contains("{fc2}") {
- print_warning(
- "You must provide the '{fc2}' string where you \
- want the 'fc2' capture to be inserted in the \
- forward check.",
- );
- return Err(ErrorKind::Parse.into());
- }
- }
- _ => {}
- }
- }
+ Ok(list.into_boxed_slice())
+ }
+ }
- let mut capture_names = regex.capture_names();
- if capture_names.any(|c| c.is_some() && c.unwrap() == "fc2") &&
- !capture_names.any(|c| c.is_some() && c.unwrap() == "fc1")
- {
- print_warning(
- "You must have a capture group named fc1 to use the capture \
- fc2.",
- );
- return Err(ErrorKind::Parse.into());
- }
+ deserializer.deserialize_seq(RegexSeqVisitor)
+}
- Some(s.clone())
- }
- None => None,
- _ => {
- print_warning(format_warning);
- return Err(ErrorKind::Parse.into());
- }
- };
+/// Deserializes file regular expressions.
+fn deserialize_file_regex<'de, D>(deserializer: D) -> Result