├── .cargo └── config.toml ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── commisery.yml │ ├── coverage.yml │ ├── dco.yml │ ├── dependency-review.yml │ ├── lint.yml │ ├── release.yml │ ├── scorecard.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── MDB_Logo.ico ├── README.md ├── build.rs ├── client-py ├── Cargo.toml ├── README.md ├── build.rs ├── pyproject.toml └── src │ ├── cart.rs │ ├── lib.rs │ └── types.rs ├── client ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── cli │ ├── cart.rs │ ├── mod.rs │ ├── report.rs │ ├── retrieval.rs │ ├── similar.rs │ └── submit.rs │ ├── lib.rs │ ├── main.rs │ └── option_cert_path_serialization.rs ├── crates ├── api │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── digest.rs │ │ └── lib.rs ├── server │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ │ ├── crypto.rs │ │ ├── db │ │ ├── admin.rs │ │ ├── malwaredb_pg.sql │ │ ├── malwaredb_sqlite.sql │ │ ├── mod.rs │ │ ├── pg.rs │ │ ├── readme.md │ │ ├── sqlite.rs │ │ └── types.rs │ │ ├── http │ │ ├── mod.rs │ │ └── receive.rs │ │ ├── lib.rs │ │ ├── utils │ │ └── mod.rs │ │ └── vt │ │ └── mod.rs └── types │ ├── Cargo.toml │ ├── README.md │ ├── src │ ├── doc │ │ ├── mod.rs │ │ ├── office95.rs │ │ ├── pdf.rs │ │ └── rtf.rs │ ├── exec │ │ ├── elf │ │ │ ├── fields.rs │ │ │ └── mod.rs │ │ ├── macho │ │ │ ├── fat.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── pe32 │ │ │ ├── fields.rs │ │ │ ├── mod.rs │ │ │ └── utils.rs │ │ └── pef │ │ │ └── mod.rs │ ├── lib.rs │ ├── main.rs │ └── utils.rs │ └── testdata │ ├── Hello.java │ ├── class │ └── Hello.class │ ├── elf │ ├── elf_aclock_hobbit_beos │ ├── elf_freebsd_x86_64 │ ├── elf_haiku_x86 │ ├── elf_haiku_x86.cart │ ├── elf_linux_arm │ ├── elf_linux_arm64 │ ├── elf_linux_mips │ ├── elf_linux_mips64 │ ├── elf_linux_ppc64le │ ├── elf_linux_ppc64le.so │ ├── elf_linux_riscv64 │ └── elf_linux_s390x │ ├── exe │ ├── efi │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── hello.efi │ │ └── main.c │ ├── pe32_aclock_axp.exe │ ├── pe32_aclock_mips.exe │ ├── pe32_aclock_ppc_winnt.exe │ ├── pe32_dotnet.exe │ ├── pe64_aclock_axp64.exe │ ├── pe64_lib_console_x86_64_gnu.exe │ ├── pe64_lib_gui_x86_64_gnu.exe │ ├── pe64_posix_x86_64_gnu.exe │ ├── pe64_win32_console_arm64_gnu.exe │ ├── pe64_win32_console_x86_64_gnu.exe │ ├── pe64_win32_gui_x86_64_gnu.exe │ └── pe64_xbox_x86_64_gnu.exe │ ├── macho.entitlements │ ├── macho │ ├── macho_arm64 │ ├── macho_arm64_signed │ ├── macho_fat_arm64_ppc_ppc64_x86_64 │ ├── macho_fat_arm64_x86_64 │ ├── macho_ppc │ ├── macho_ppc64 │ └── macho_x86_64 │ ├── office95 │ ├── excel.xls │ ├── powerpoint.ppt │ └── word.doc │ ├── office_zip │ ├── excel.xlsx │ ├── excel.xltx │ ├── powerpoint.potx │ ├── powerpoint.pptx │ ├── word.docx │ └── word.dotx │ ├── pdf │ ├── test.pdf │ ├── test_archival.pdf │ └── test_form_barcode.pdf │ ├── pef │ ├── BeApp │ ├── MacOS_1 │ └── MacOS_2 │ ├── readme.md │ ├── rtf │ ├── empty.rtf │ └── hello.rtf │ ├── source.c │ ├── source.cs │ ├── source_lib.c │ └── zip │ └── source.c.zip ├── deny.toml ├── log.md ├── mdb_server_config.toml.example ├── src ├── cli │ ├── admin │ │ ├── compression.rs │ │ ├── config.rs │ │ ├── groups.rs │ │ ├── keys.rs │ │ ├── labels.rs │ │ ├── load.rs │ │ ├── mod.rs │ │ ├── sources.rs │ │ ├── users.rs │ │ └── vt.rs │ ├── cart_io.rs │ ├── config.rs │ ├── mod.rs │ ├── run.rs │ ├── service │ │ ├── freebsd.rs │ │ ├── launchctl.rs │ │ ├── mod.rs │ │ ├── other.rs │ │ └── systemd.rs │ └── vt │ │ ├── data_loader.rs │ │ ├── mod.rs │ │ └── updater.rs ├── gui │ ├── admin.slint │ └── mod.rs └── main.rs └── testdata ├── .gitignore ├── ca_cert.pem ├── make_certs.sh ├── server_ca_cert.pem ├── server_cert.der ├── server_cert.pem ├── server_key.der └── server_key.pem /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Goals: Boost performance if we can 2 | # Concerns: don't make MalwareDB dependent on the lastest and greatest, support older 64-bit hardware 3 | 4 | [target.aarch64-apple-darwin] 5 | rustflags = ["-C", "target-feature=+aes,+crc,+neon,+sha2"] 6 | 7 | [target.aarch64-pc-windows-msvc] 8 | rustflags = ["-C", "target-feature=+aes,+crc,+neon,+sha2"] 9 | 10 | [target.aarch64-unknown-linux-gnu] 11 | rustflags = ["-C", "target-feature=+aes,+crc,+neon,+sha2"] 12 | 13 | [target.aarch64-unknown-linux-musl] 14 | rustflags = ["-C", "target-feature=+aes,+crc,+neon,+sha2"] 15 | 16 | [target.powerpc64-unknown-linux-gnu] 17 | rustflags = ["-C", "target-feature=+power8-crypto,+vsx"] 18 | 19 | [target.powerpc64-unknown-linux-musl] 20 | rustflags = ["-C", "target-feature=+power8-crypto,+vsx"] 21 | 22 | [target.powerpc64le-unknown-linux-gnu] 23 | rustflags = ["-C", "target-feature=+power8-crypto,+vsx"] 24 | 25 | [target.powerpc64le-unknown-linux-musl] 26 | rustflags = ["-C", "target-feature=+power8-crypto,+vsx"] 27 | 28 | [target.x86_64-apple-darwin] 29 | rustflags = ["-C", "target-feature=+aes,+fma,+sha,+sse4.2,+popcnt"] 30 | 31 | [target.x86_64-pc-windows-gnu] 32 | rustflags = ["-C", "target-feature=+aes,+fma,+sha,+sse4.2,+popcnt"] 33 | 34 | [target.x86_64-pc-windows-msvc] 35 | rustflags = ["-C", "target-feature=+aes,+fma,+sha,+sse4.2,+popcnt"] 36 | 37 | [target.x86_64-unknown-freebsd] 38 | rustflags = ["-C", "target-feature=+aes,+fma,+sha,+sse4.2,+popcnt"] 39 | 40 | [target.x86_64-unknown-linux-gnu] 41 | rustflags = ["-C", "target-feature=+aes,+fma,+sha,+sse4.2,+popcnt"] 42 | 43 | [target.x86_64-unknown-linux-musl] 44 | rustflags = ["-C", "target-feature=+aes,+fma,+sha,+sse4.2,+popcnt"] 45 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @rjzak 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: malwaredb 2 | liberapay: rjzak 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/workflows/commisery.yml: -------------------------------------------------------------------------------- 1 | name: Commisery 2 | on: 3 | workflow_call: 4 | pull_request: 5 | types: [edited, opened, synchronize, reopened] 6 | 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | # This workflow uses a third-party action. 13 | # The existence of a `permissions` key denies all 14 | # permissions except those explicitly specified. 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | issues: write 19 | 20 | jobs: 21 | commit-message: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Harden Runner 25 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 26 | with: 27 | disable-sudo: true 28 | egress-policy: block 29 | allowed-endpoints: > 30 | api.github.com:443 31 | 32 | - name: Run Commisery 33 | uses: tomtom-international/commisery-action@f830c46b56b05c427cb2b5b650d848bee1681c99 # v3.5.0 34 | with: 35 | token: ${{ secrets.GITHUB_TOKEN }} 36 | validate-pull-request: false 37 | validate-pull-request-title-bump: false 38 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | on: [ pull_request, push ] 3 | permissions: 4 | contents: read 5 | jobs: 6 | coverage: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Harden Runner 10 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 11 | with: 12 | egress-policy: audit 13 | allowed-endpoints: > 14 | api.github.com:443 15 | cli.codecov.io:443 16 | codecov.io:443 17 | crates.io:443 18 | github.com:443 19 | index.crates.io:443 20 | kv4gacprodeus2file3.blob.core.windows.net:443 21 | objects.githubusercontent.com:443 22 | static.crates.io:443 23 | static.rust-lang.org:443 24 | storage.googleapis.com:443 25 | uploader.codecov.io:443 26 | 27 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 28 | - name: Install cargo-llvm-cov 29 | uses: taiki-e/install-action@6e2226b90b9a51735a2363fdd379c7545cfd8748 # cargo-llvm-cov 30 | - name: Install dependencies 31 | run: sudo apt-get install -y libmagic-dev 32 | - name: Generate code coverage 33 | run: cargo llvm-cov --features=sqlite --workspace --lcov --output-path lcov.info 34 | - name: Upload coverage to Codecov 35 | uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 36 | with: 37 | token: ${{ secrets.CODECOV_TOKEN }} 38 | files: lcov.info 39 | fail_ci_if_error: false 40 | -------------------------------------------------------------------------------- /.github/workflows/dco.yml: -------------------------------------------------------------------------------- 1 | name: DCO Check 2 | on: [pull_request] 3 | permissions: read-all 4 | jobs: 5 | dco: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Harden Runner 9 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 10 | with: 11 | disable-sudo: true 12 | egress-policy: block 13 | allowed-endpoints: > 14 | api.github.com:443 15 | 16 | - uses: tisonkun/actions-dco@f1024cd563550b5632e754df11b7d30b73be54a5 # v1.1 17 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [ pull_request ] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 21 | with: 22 | disable-sudo: true 23 | egress-policy: block 24 | allowed-endpoints: > 25 | api.github.com:443 26 | api.deps.dev:443 27 | github.com:443 28 | 29 | - name: 'Checkout Repository' 30 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 31 | - name: 'Dependency Review' 32 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 33 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: [ push, pull_request ] 3 | permissions: 4 | contents: read 5 | jobs: 6 | rust: 7 | name: Cargo clippy & fmt 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Harden Runner 11 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 12 | with: 13 | egress-policy: block 14 | allowed-endpoints: > 15 | azure.archive.ubuntu.com:80 16 | crates.io:443 17 | esm.ubuntu.com:443 18 | github.com:443 19 | githubapp.com:443 20 | index.crates.io:443 21 | motd.ubuntu.com:443 22 | ppa.launchpadcontent.net:443 23 | static.crates.io:443 24 | static.rust-lang.org:443 25 | 26 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 27 | - name: Setup Rust toolchain 28 | run: rustup show && rustup update 29 | - name: Install GUI dependencies 30 | run: sudo apt-get install -y libxcb-shape0-dev libxcb-xfixes0-dev libx11-dev libxkbcommon-dev libfontconfig-dev libxext-dev libxft-dev libxinerama-dev libxcursor-dev libxrender-dev libxfixes-dev 31 | - name: cargo fmt 32 | run: cargo fmt -- --check 33 | - name: cargo clippy 34 | run: cargo clippy --workspace --all-features --tests -- -D warnings 35 | 36 | check-spdx-headers: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | - uses: enarx/spdx@b5bfdd4410071bf058c8333d0e70020001524b6b # master 41 | with: 42 | licenses: Apache-2.0 43 | 44 | udeps: 45 | name: Unused dependencies 46 | runs-on: ubuntu-latest 47 | steps: 48 | - name: Harden Runner 49 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 50 | with: 51 | egress-policy: block 52 | allowed-endpoints: > 53 | api.github.com:443 54 | crates.io:443 55 | github.com:443 56 | githubapp.com:443 57 | index.crates.io:443 58 | objects.githubusercontent.com:443 59 | static.crates.io:443 60 | static.rust-lang.org:443 61 | 62 | - name: Checkout repository 63 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 64 | 65 | - name: Install nightly and cargo-udeps 66 | run: | 67 | rustup update 68 | rustup toolchain install nightly 69 | sudo apt-get install -y wget 70 | wget https://github.com/est31/cargo-udeps/releases/download/v0.1.55/cargo-udeps-v0.1.55-x86_64-unknown-linux-musl.tar.gz 71 | tar -xzf cargo-udeps-v0.1.55-x86_64-unknown-linux-musl.tar.gz 72 | mv cargo-udeps-v0.1.55-x86_64-unknown-linux-musl/cargo-udeps ~/.cargo/bin 73 | cargo +nightly udeps 74 | 75 | audit: 76 | name: Cargo Audit 77 | runs-on: ubuntu-latest 78 | steps: 79 | - name: Harden Runner 80 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 81 | with: 82 | disable-sudo: true 83 | egress-policy: block 84 | allowed-endpoints: > 85 | api.github.com:443 86 | crates.io:443 87 | github.com:443 88 | githubapp.com:443 89 | index.crates.io:443 90 | objects.githubusercontent.com:443 91 | static.crates.io:443 92 | 93 | - name: Checkout repository 94 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 95 | 96 | - name: Install Cargo Audit 97 | run: | 98 | wget https://github.com/rustsec/rustsec/releases/download/cargo-audit%2Fv0.21.2/cargo-audit-x86_64-unknown-linux-musl-v0.21.2.tgz 99 | tar -xzf cargo-audit-x86_64-unknown-linux-musl-v0.21.2.tgz 100 | mv cargo-audit-x86_64-unknown-linux-musl-v0.21.2/cargo-audit ~/.cargo/bin/ 101 | 102 | - name: Run Cargo Audit 103 | run: cargo audit 104 | 105 | hack: 106 | name: Cargo Hack 107 | runs-on: ubuntu-latest 108 | steps: 109 | - name: Harden Runner 110 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 111 | with: 112 | disable-sudo: true 113 | egress-policy: block 114 | allowed-endpoints: > 115 | crates.io:443 116 | github.com:443 117 | githubapp.com:443 118 | index.crates.io:443 119 | objects.githubusercontent.com:443 120 | static.crates.io:443 121 | 122 | - name: Checkout repository 123 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 124 | 125 | - name: Install Cargo Hack 126 | uses: taiki-e/install-action@48d3f75c40e16665338fbdba67565ae6e6ae6749 # cargo-hack 127 | 128 | - name: Run Cargo Hack on MalwareDB 129 | run: cargo hack check --each-feature --no-dev-deps 130 | 131 | - name: Run Cargo Hack on malwaredb-types 132 | run: | 133 | cd crates/types 134 | cargo hack check --each-feature --no-dev-deps 135 | 136 | - name: Run Cargo Hack on malwaredb-server 137 | run: | 138 | cd crates/server 139 | cargo hack check --each-feature --no-dev-deps 140 | 141 | - name: Run Cargo Hack on malwaredb-client 142 | run: | 143 | cd client 144 | cargo hack check --each-feature --no-dev-deps 145 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | name: OSSF supply-chain security 2 | on: 3 | branch_protection_rule: 4 | schedule: 5 | - cron: '31 6 * * 0' 6 | push: 7 | branches: [ "main" ] 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | analysis: 13 | name: Scorecard analysis 14 | runs-on: ubuntu-latest 15 | permissions: 16 | security-events: write 17 | id-token: write 18 | 19 | steps: 20 | - name: Harden Runner 21 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 22 | with: 23 | disable-sudo: true 24 | egress-policy: block 25 | allowed-endpoints: > 26 | api.deps.dev:443 27 | api.github.com:443 28 | api.osv.dev:443 29 | api.scorecard.dev:443 30 | api.securityscorecards.dev:443 31 | fulcio.sigstore.dev:443 32 | github.com:443 33 | oss-fuzz-build-logs.storage.googleapis.com:443 34 | rekor.sigstore.dev:443 35 | tuf-repo-cdn.sigstore.dev:443 36 | www.bestpractices.dev:443 37 | 38 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 39 | with: 40 | persist-credentials: false 41 | 42 | - name: Run analysis 43 | uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 44 | with: 45 | results_file: results.sarif 46 | results_format: sarif 47 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 48 | # - you want to enable the Branch-Protection check on a *public* repository, or 49 | # - you are installing Scorecard on a *private* repository 50 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 51 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 52 | 53 | # Public repositories: 54 | # - Publish results to OpenSSF REST API for easy access by consumers 55 | # - Allows the repository to include the Scorecard badge. 56 | # - See https://github.com/ossf/scorecard-action#publishing-results. 57 | # For private repositories: 58 | # - `publish_results` will always be set to `false`, regardless 59 | # of the value entered here. 60 | publish_results: true 61 | 62 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 63 | # format to the repository Actions tab. 64 | - name: Upload artifact 65 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 66 | with: 67 | name: SARIF file 68 | path: results.sarif 69 | retention-days: 5 70 | 71 | # Upload the results to GitHub's code scanning dashboard. 72 | - name: Upload to code-scanning 73 | uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 74 | with: 75 | sarif_file: results.sarif 76 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [ push, pull_request ] 3 | permissions: 4 | contents: read 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Harden Runner 10 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 11 | with: 12 | egress-policy: block 13 | allowed-endpoints: > 14 | azure.archive.ubuntu.com:80 15 | crates.io:443 16 | esm.ubuntu.com:443 17 | github.com:443 18 | githubapp.com:443 19 | index.crates.io:443 20 | motd.ubuntu.com:443 21 | ppa.launchpadcontent.net:443 22 | static.crates.io:443 23 | static.rust-lang.org:443 24 | 25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: Update Rust toolchain 27 | run: rustup update 28 | - name: Install dependencies 29 | run: sudo apt-get install -y libmagic-dev 30 | - name: Install GUI dependencies 31 | run: | 32 | sudo apt-get update 33 | sudo apt-get install -y libxcb-shape0-dev libxcb-xfixes0-dev libx11-dev libxkbcommon-dev libfontconfig-dev libxext-dev libxft-dev libxinerama-dev libxcursor-dev libxrender-dev libxfixes-dev 34 | - name: Build client 35 | run: cargo build --workspace --bin mdb_client 36 | - name: Build server 37 | run: cargo build --features=admin,admin-gui,sqlite,vt --bin mdb_server 38 | - name: cargo test 39 | run: cargo test --workspace 40 | 41 | #postgres: 42 | # runs-on: ubuntu-latest 43 | # services: 44 | # postgres: 45 | # image: ghcr.io/malwaredb/docker:main 46 | # env: 47 | # POSTGRES_PASSWORD: malwaredbtesting 48 | # POSTGRES_HOST_AUTH_METHOD: trust 49 | # POSTGRES_DB: malwaredbtesting 50 | # POSTGRES_USER: malwaredbtesting 51 | # ports: 52 | # - 5432:5432 53 | # options: >- 54 | # --health-cmd pg_isready 55 | # --health-interval 10s 56 | # --health-timeout 5s 57 | # --health-retries 5 58 | # -h 0.0.0.0 59 | # steps: 60 | # - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 61 | #- name: Create database 62 | # run: | 63 | # PGPASSWORD=malwaredbtesting psql -h 127.0.0.1 -p ${{ job.services.postgres.ports[5432] }} -tc "create user malwaredbtesting with password 'malwaredbtesting';" 64 | # PGPASSWORD=malwaredbtesting psql -h 127.0.0.1 -p ${{ job.services.postgres.ports[5432] }} -tc "create database malwaredbtesting owner malwaredbtesting;" 65 | # - name: Load extensions 66 | # run: | 67 | # PGPASSWORD=malwaredbtesting psql -U malwaredbtesting -h 127.0.0.1 -p ${{ job.services.postgres.ports[5432] }} -d malwaredbtesting -tc "CREATE OR REPLACE FUNCTION fuzzy_hash_compare(TEXT, TEXT) RETURNS INTEGER AS 'ssdeep_psql.so', 'pg_fuzzy_hash_compare' LANGUAGE 'c';" 68 | # PGPASSWORD=malwaredbtesting psql -U malwaredbtesting -h 127.0.0.1 -p ${{ job.services.postgres.ports[5432] }} -d malwaredbtesting -tc "CREATE OR REPLACE FUNCTION tlsh_compare(TEXT, TEXT) RETURNS INTEGER AS 'tlsh_psql.so', 'pg_tlsh_compare' LANGUAGE 'c';" 69 | # PGPASSWORD=malwaredbtesting psql -U malwaredbtesting -h 127.0.0.1 -p ${{ job.services.postgres.ports[5432] }} -d malwaredbtesting -tc "CREATE OR REPLACE FUNCTION sdhash_compare(TEXT, TEXT) RETURNS INTEGER AS 'sdhash_psql.so', 'pg_sdhash_compare' LANGUAGE 'c';" 70 | # PGPASSWORD=malwaredbtesting psql -U malwaredbtesting -h 127.0.0.1 -p ${{ job.services.postgres.ports[5432] }} -d malwaredbtesting -tc "CREATE OR REPLACE FUNCTION lzjd_compare(TEXT, TEXT) RETURNS INTEGER AS 'lzjd_psql.so', 'pg_lzjd_compare' LANGUAGE 'c';" 71 | # - name: MalwareDB postgres test 72 | # run: | 73 | # PG_PORT=${{ job.services.postgres.ports[5432] }} cargo test db --workspace -- --ignored 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | lconv.info 4 | *.db 5 | *.json 6 | *.xml 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gitleaks/gitleaks 3 | rev: v8.16.3 4 | hooks: 5 | - id: gitleaks 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.4.0 8 | hooks: 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | -------------------------------------------------------------------------------- /MDB_Logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malwaredb/malwaredb-rs/41b409122d8f914013bae62da1fa16e9084c5027/MDB_Logo.ico -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; 4 | 5 | fn main() { 6 | #[cfg(feature = "admin-gui")] 7 | slint_build::compile("src/gui/admin.slint").unwrap(); 8 | 9 | #[cfg(target_os = "windows")] 10 | { 11 | let mut res = winresource::WindowsResource::new(); 12 | res.set_icon("MDB_Logo.ico"); 13 | res.compile().unwrap(); 14 | } 15 | 16 | let mut git = GitclBuilder::default(); 17 | let git = git.all().describe(false, true, None).build().unwrap(); 18 | 19 | Emitter::default() 20 | .add_instructions(&BuildBuilder::all_build().unwrap()) 21 | .unwrap() 22 | .add_instructions(&git) 23 | .unwrap() 24 | .emit() 25 | .unwrap(); 26 | } 27 | -------------------------------------------------------------------------------- /client-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "malwaredb-client-py" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | authors.workspace = true 7 | repository.workspace = true 8 | homepage.workspace = true 9 | description = "Python client for MalwareDB." 10 | build = "build.rs" 11 | keywords.workspace = true 12 | categories.workspace = true 13 | 14 | [features] 15 | default = [] 16 | rust_lib = [] 17 | 18 | [dependencies] 19 | # Internal dependencies 20 | malwaredb-client = { workspace = true } 21 | 22 | # External dependencies 23 | anyhow = { workspace = true, features = ["std"] } 24 | pyo3 = { workspace = true, features = ["abi3", "anyhow", "experimental-async", "extension-module", "pyo3-macros"] } 25 | 26 | [build-dependencies] 27 | pyo3-build-config = { workspace = true } 28 | vergen = { workspace = true, features = ["build"] } 29 | vergen-gitcl = { workspace = true, features = ["build"] } 30 | 31 | [lib] 32 | name = "malwaredb" 33 | crate-type = ["cdylib", "rlib"] 34 | -------------------------------------------------------------------------------- /client-py/README.md: -------------------------------------------------------------------------------- 1 | ## Malware DB Python Client 2 | 3 | This is a Python module for interacting with an instance of Malware DB. 4 | 5 | ### Installation 6 | Assuming you already have Malware DB checked out: 7 | 8 | 1. `python3 -m venv mdb-venv` 9 | 2. `source mdb-venv/bin/activate` 10 | 3. `pip install maturin` 11 | 4. `cd client-py` 12 | 5. `maturin develop` 13 | 14 | ### Features 15 | If using this crate with another library, be sure to use the `rust_lib` feature flag to avoid creating the Malware DB client module type. 16 | 17 | ### Use 18 | 1. Import the module: `from malwaredb import MalwareDBClient` 19 | 2. Create a client object from one of three ways: 20 | * `MalwareDBClient.from_file()` to use a configuration file 21 | * `MalwareDBClient()` to have the client library find a configuration file 22 | * `MalwareDBClient.connect(url, api_key, [cert_path])` to provide the URL, API key, and optional path for a certificate for encrypted communication 23 | * `MalwareDBClient.login(url, username, password, save, [cert_path])` to provide the URL, user name, password, whether to save the configuration, and optional path for a certificate for encrypted communication 24 | 3. Client functionality: 25 | * `.get_file_bytes()` get a file from Malware DB 26 | * `.server_info()` get server information 27 | * `.submit_file()` submit a file to Malware DB 28 | * `.labels()` get available labels for samples 29 | * `.whoami()` get information about the user's account 30 | -------------------------------------------------------------------------------- /client-py/build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; 4 | 5 | fn main() { 6 | pyo3_build_config::add_extension_module_link_args(); 7 | 8 | let mut git = GitclBuilder::default(); 9 | let git = git.all().describe(false, true, None).build().unwrap(); 10 | 11 | Emitter::default() 12 | .add_instructions(&BuildBuilder::all_build().unwrap()) 13 | .unwrap() 14 | .add_instructions(&git) 15 | .unwrap() 16 | .emit() 17 | .unwrap(); 18 | } 19 | -------------------------------------------------------------------------------- /client-py/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1,<2"] 3 | build-backend = "maturin" 4 | 5 | [project] 6 | name = "malwaredb" 7 | description = "Python client for MalwareDB" 8 | readme = "README.md" 9 | license = "Apache-2.0" 10 | dynamic = ["version"] 11 | requires-python = ">=3.7" 12 | authors = [ { name = "Richard Zak", email = "richard.j.zak@gmail.com" } ] 13 | keywords = ["cybersecurity", "database", "forensics", "malware", "research"] 14 | classifiers = [ 15 | "Development Status :: 3 - Alpha", 16 | "Intended Audience :: Information Technology", 17 | "Intended Audience :: Science/Research", 18 | "License :: OSI Approved :: Apache Software License", 19 | "Programming Language :: Rust", 20 | "Programming Language :: Python :: Implementation :: CPython", 21 | "Programming Language :: Python :: Implementation :: PyPy", 22 | ] 23 | 24 | [project.urls] 25 | Homepage = "https://malwaredb.net" 26 | Repository = "https://github.com/malwaredb/malwaredb-rs" 27 | Issues = "https://github.com/malwaredb/malwaredb-rs/issues" 28 | Changelog = "https://github.com/malwaredb/malwaredb-rs/blob/main/log.md" 29 | -------------------------------------------------------------------------------- /client-py/src/cart.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use anyhow::Result; 4 | use pyo3::prelude::*; 5 | 6 | /// Encase a binary in a `CaRT` file 7 | /// 8 | /// # Errors 9 | /// 10 | /// There should not be any errors, but the underlying library can't guarantee that. However, any data 11 | /// passed to it is correct. 12 | #[pyfunction] 13 | pub fn create_cart(data: &[u8]) -> Result> { 14 | malwaredb_client::encode_to_cart(data) 15 | } 16 | 17 | /// Registers the `CaRT` module to an exiting module 18 | /// 19 | /// # Errors 20 | /// 21 | /// Should not have errors but not yet known 22 | #[cfg(not(feature = "rust_lib"))] 23 | pub fn register_cart_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> { 24 | let child_module = PyModule::new(parent_module.py(), "cart")?; 25 | child_module.add_function(wrap_pyfunction!(create_cart, &child_module)?)?; 26 | parent_module.add_submodule(&child_module) 27 | } 28 | -------------------------------------------------------------------------------- /client-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #![doc = include_str!("../README.md")] 4 | #![deny(clippy::all)] 5 | #![deny(clippy::pedantic)] 6 | #![forbid(unsafe_code)] 7 | 8 | /// `CaRT` file I/O 9 | pub mod cart; 10 | 11 | /// Python wrapper types for some Malware DB API types 12 | pub mod types; 13 | 14 | use std::path::PathBuf; 15 | 16 | use crate::types::{Label, ServerInfo, Source, SupportedFileType, UserInfo}; 17 | use malwaredb_client::MdbClient; 18 | 19 | use anyhow::Result; 20 | use pyo3::prelude::*; 21 | 22 | /// MDB version 23 | pub const MDB_VERSION: &str = env!("CARGO_PKG_VERSION"); 24 | 25 | pub const VERSION: &str = concat!( 26 | "v", 27 | env!("CARGO_PKG_VERSION"), 28 | "-", 29 | env!("VERGEN_GIT_DESCRIBE"), 30 | " ", 31 | env!("VERGEN_BUILD_DATE") 32 | ); 33 | 34 | /// Malware DB client 35 | #[pyclass] 36 | struct MalwareDBClient { 37 | inner: MdbClient, 38 | } 39 | 40 | #[pymethods] 41 | impl MalwareDBClient { 42 | /// Load a configuration from a file if it can be found 43 | #[new] 44 | pub fn new() -> PyResult { 45 | Ok(MalwareDBClient { 46 | inner: MdbClient::load()?, 47 | }) 48 | } 49 | 50 | /// Login with a username and password 51 | #[staticmethod] 52 | pub async fn login( 53 | url: String, 54 | username: String, 55 | password: String, 56 | save: bool, 57 | cert_path: Option, 58 | ) -> PyResult { 59 | Ok(MalwareDBClient { 60 | inner: MdbClient::login(url, username, password, save, cert_path).await?, 61 | }) 62 | } 63 | 64 | /// Connect if an API key is already known 65 | #[staticmethod] 66 | pub fn connect(url: String, api_key: String, cert_path: Option) -> PyResult { 67 | Ok(MalwareDBClient { 68 | inner: MdbClient::new(url, api_key, cert_path)?, 69 | }) 70 | } 71 | 72 | /// Connect using a specific configuration file 73 | #[staticmethod] 74 | pub fn from_file(path: PathBuf) -> Result { 75 | Ok(MalwareDBClient { 76 | inner: MdbClient::from_file(path)?, 77 | }) 78 | } 79 | 80 | #[getter] 81 | pub fn url(&self) -> String { 82 | self.inner.url.clone() 83 | } 84 | 85 | /// Get the bytes of a sample from the database 86 | pub async fn get_file_bytes(&self, hash: String) -> Result> { 87 | self.inner.retrieve(&hash, false).await 88 | } 89 | 90 | /// Submit a file to the database, which requires the file name and source ID. Returns true if stored. 91 | pub async fn submit_file( 92 | &self, 93 | contents: Vec, 94 | file_name: String, 95 | source_id: u32, 96 | ) -> Result { 97 | self.inner.submit(&contents, &file_name, source_id).await 98 | } 99 | 100 | /// Get sources available to the user 101 | pub async fn get_sources(&self) -> Result> { 102 | let sources = self 103 | .inner 104 | .sources() 105 | .await? 106 | .sources 107 | .iter() 108 | .map(|s| Source { 109 | id: s.id, 110 | name: s.name.clone(), 111 | description: s.description.clone(), 112 | url: s.url.clone(), 113 | first_acquisition: s.first_acquisition.to_rfc3339(), 114 | malicious: s.malicious, 115 | }) 116 | .collect(); 117 | Ok(sources) 118 | } 119 | 120 | /// Get information about the server 121 | pub async fn server_info(&self) -> Result { 122 | let info = self.inner.server_info().await?; 123 | Ok(ServerInfo { 124 | os_name: info.os_name, 125 | memory_used: info.memory_used, 126 | mdb_version: info.mdb_version, 127 | db_version: info.db_version, 128 | db_size: info.db_size, 129 | num_samples: info.num_samples, 130 | num_users: info.num_users, 131 | uptime: info.uptime, 132 | }) 133 | } 134 | 135 | /// Get supported file types; Malware DB only accepts file types it knows about 136 | pub async fn get_supported_file_types(&self) -> Result> { 137 | let supported_types = self 138 | .inner 139 | .supported_types() 140 | .await? 141 | .types 142 | .iter() 143 | .map(|t| SupportedFileType { 144 | name: t.name.clone(), 145 | magic: t.magic.clone(), 146 | is_executable: t.is_executable, 147 | description: t.description.clone(), 148 | }) 149 | .collect(); 150 | Ok(supported_types) 151 | } 152 | 153 | /// Get information about the user 154 | pub async fn whoami(&self) -> Result { 155 | self.inner.whoami().await.map(|w| UserInfo { 156 | id: w.id, 157 | username: w.username, 158 | groups: w.groups, 159 | sources: w.sources, 160 | is_admin: w.is_admin, 161 | created: w.created.to_rfc3339(), 162 | is_readonly: w.is_readonly, 163 | }) 164 | } 165 | 166 | /// Get labels 167 | pub async fn labels(&self) -> Result> { 168 | self.inner.labels().await.map(|labels| { 169 | labels 170 | .0 171 | .iter() 172 | .map(|l| Label { 173 | id: l.id, 174 | name: l.name.clone(), 175 | parent: l.parent.clone(), 176 | }) 177 | .collect() 178 | }) 179 | } 180 | } 181 | 182 | #[cfg(not(feature = "rust_lib"))] 183 | #[pymodule] 184 | fn malwaredb(m: &Bound<'_, PyModule>) -> PyResult<()> { 185 | m.add_class::()?; 186 | m.add_class::