virustotal-api-1.1.11/0000755000076600000240000000000013541733575015516 5ustar blacktopstaff00000000000000virustotal-api-1.1.11/HISTORY.rst0000644000076600000240000000476713541733224017416 0ustar blacktopstaff00000000000000.. :changelog: Release History --------------- 1.1.11 (2019-09-22) ------------------- **Allow for hash list input in get_file_report** - https://github.com/blacktop/virustotal-api/pull/28 (credit: @CDuPlooy) 1.1.10 (2018-03-12) ------------------- **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/23 (credit: @leadZERO) 1.1.9 (2018-01-03 aka the day the CPUs fell) -------------------------------------------- **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/22 (credit: @leadZERO) 1.1.7 (2017-05-28) ------------------ **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/18 (credit: @doug-the-guy) 1.1.6 (2017-05-14) ------------------ **Py3 Fix** - Change `e.message` to `str(message)` (credit: [@DeanF](https://github.com/blacktop/virustotal-api/pull/19)) 1.1.5 (2017-04-13) ------------------ **API Changes** - Added Intelligence notifications feed and ability to programmatically delete notifications from the feed. (credit: @keithjjones) 1.1.4 (2017-03-11) ------------------ **Fixed timeout functionality, removed unnecessary methods** - Fixed the timeout parameter in the PublicApi and removes unnecessary code in the PrivateApi (credit: @mrredamber aka LEGEND) 1.1.3 (2017-02-03) ------------------ **Request Timeout Functionality** - Adds a timeout parameter to methods that make requests to the VirusTotal API (credit: @mrredamber aka LEGEND) 1.1.2 (2016-04-13) ------------------ **API Changes** - Re-adding the ability to use files from memory as well as from disk. (credit: @tweemeterjop) 1.1.1 (2016-03-13) ------------------ **API Changes** - Adding file/url feed private API endpoint. 1.0.9 (2016-01-01) ------------------ **Privacyfixes** - Fix scan_file (upload to VT), do not leak full path. (credit: @Rafiot) 1.0.8 (2014-12-26) ------------------ **Bugfixes** - Fixed get_url_report method for the Private API (credit: @John-Lin) 1.0.7 (2014-10-17) ------------------ **Bugfixes** - Fixed get_network_traffic method to return the pcap data (credit: adrianherrera) 1.0.6 (2014-09-22) ------------------ **Bugfixes** - Fixed a small typo in the private API's scan_file method (credit: adrianherrera) 1.0.5 (2014-05-18) ------------------ **Bugfixes** - Fixing README.rst for better PYPI presentation. 1.0.2 (2014-05-18) ------------------ **API Changes** - Changing folder structure so when people import it it is not dumb :( 1.0.1 (2014-04-14) ------------------ **Bugfixes** - Trying to fix setup.py for deploying to PYPI. virustotal-api-1.1.11/LICENSE0000644000076600000240000000207013074026667016520 0ustar blacktopstaff00000000000000The MIT License (MIT) Copyright (c) 2014-2017 blacktop Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. virustotal-api-1.1.11/MANIFEST.in0000644000076600000240000000005613074026667017253 0ustar blacktopstaff00000000000000include README.rst LICENSE NOTICE HISTORY.rst virustotal-api-1.1.11/NOTICE0000644000076600000240000000126713074026667016426 0ustar blacktopstaff00000000000000virustotal-api includes some vendorized python libraries to ease installation. Requests License =============== Copyright 2014 Kenneth Reitz Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. virustotal-api-1.1.11/PKG-INFO0000644000076600000240000002267413541733575016626 0ustar blacktopstaff00000000000000Metadata-Version: 1.1 Name: virustotal-api Version: 1.1.11 Summary: Virus Total Public/Private/Intel API Home-page: https://github.com/blacktop/virustotal-api Author: blacktop Author-email: dev@blacktop.io License: MIT Description: .. image:: https://raw.githubusercontent.com/blacktop/virustotal-api/master/doc/logo.png virustotal-api ============== .. image:: https://travis-ci.org/blacktop/virustotal-api.svg?branch=master :target: https://travis-ci.org/blacktop/virustotal-api .. image:: http://img.shields.io/:license-mit-blue.svg :target: http://doge.mit-license.org .. image:: https://img.shields.io/pypi/v/virustotal-api.svg :target: https://pypi.python.org/pypi/virustotal-api/ .. image:: https://img.shields.io/pypi/pyversions/virustotal-api.svg :target: https://pypi.python.org/pypi/virustotal-api/ Virus Total Public/Private/Intel API - https://www.virustotal.com/en/documentation/public-api/ - https://www.virustotal.com/en/documentation/private-api/ - https://www.virustotal.com/intelligence/help/automation/ Installation ------------ .. code-block:: bash $ pip install virustotal-api Usage ----- .. code-block:: python from __future__ import print_function import json import hashlib from virus_total_apis import PublicApi as VirusTotalPublicApi API_KEY = 'Sign-Up for API Key at virustotal.com' EICAR = "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".encode('utf-8') EICAR_MD5 = hashlib.md5(EICAR).hexdigest() vt = VirusTotalPublicApi(API_KEY) response = vt.get_file_report(EICAR_MD5) print(json.dumps(response, sort_keys=False, indent=4)) Output: ------- .. code-block:: json { "response_code": 200, "results": { "scan_id": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1397510237", "sha1": "3395856ce81f2b7382dee72602f798b642f14140", "resource": "44d88612fea8a8f36de82e1278abb02f", "response_code": 1, "scan_date": "2014-04-14 21:17:17", "permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1397510237/", "verbose_msg": "Scan finished, scan information embedded in this object", "sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f", "positives": 49, "total": 51, "md5": "44d88612fea8a8f36de82e1278abb02f", "scans": { "Bkav": { "detected": true, "version": "1.3.0.4959", "result": "DOS.EiracA.Trojan", "update": "20140412" }, "MicroWorld-eScan": { "detected": true, "version": "12.0.250.0", "result": "EICAR-Test-File", "update": "20140414" }, "nProtect": { "detected": true, "version": "2014-04-14.02", "result": "EICAR-Test-File", "update": "20140414" }, ...... "AVG": { "detected": true, "version": "13.0.0.3169", "result": "EICAR_Test", "update": "20140414" }, "Panda": { "detected": true, "version": "10.0.3.5", "result": "EICAR-AV-TEST-FILE", "update": "20140414" }, "Qihoo-360": { "detected": true, "version": "1.0.0.1015", "result": "Trojan.Generic", "update": "20140414" } } } } Testing ------- To run the tests: .. code-block:: bash $ ./tests Documentation ------------- You're looking at it. Issues ------ Find a bug? Want more features? Find something missing in the documentation? Let me know! Please don't hesitate to `file an issue `_ and I'll get right on it. Contributing ------------ `See all contributors on GitHub `_. Please update the `HISTORY.rst `_, and submit a `Pull Request on GitHub `_. License ------- MIT Copyright (c) 2014-2019 **blacktop** .. :changelog: Release History --------------- 1.1.11 (2019-09-22) ------------------- **Allow for hash list input in get_file_report** - https://github.com/blacktop/virustotal-api/pull/28 (credit: @CDuPlooy) 1.1.10 (2018-03-12) ------------------- **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/23 (credit: @leadZERO) 1.1.9 (2018-01-03 aka the day the CPUs fell) -------------------------------------------- **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/22 (credit: @leadZERO) 1.1.7 (2017-05-28) ------------------ **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/18 (credit: @doug-the-guy) 1.1.6 (2017-05-14) ------------------ **Py3 Fix** - Change `e.message` to `str(message)` (credit: [@DeanF](https://github.com/blacktop/virustotal-api/pull/19)) 1.1.5 (2017-04-13) ------------------ **API Changes** - Added Intelligence notifications feed and ability to programmatically delete notifications from the feed. (credit: @keithjjones) 1.1.4 (2017-03-11) ------------------ **Fixed timeout functionality, removed unnecessary methods** - Fixed the timeout parameter in the PublicApi and removes unnecessary code in the PrivateApi (credit: @mrredamber aka LEGEND) 1.1.3 (2017-02-03) ------------------ **Request Timeout Functionality** - Adds a timeout parameter to methods that make requests to the VirusTotal API (credit: @mrredamber aka LEGEND) 1.1.2 (2016-04-13) ------------------ **API Changes** - Re-adding the ability to use files from memory as well as from disk. (credit: @tweemeterjop) 1.1.1 (2016-03-13) ------------------ **API Changes** - Adding file/url feed private API endpoint. 1.0.9 (2016-01-01) ------------------ **Privacyfixes** - Fix scan_file (upload to VT), do not leak full path. (credit: @Rafiot) 1.0.8 (2014-12-26) ------------------ **Bugfixes** - Fixed get_url_report method for the Private API (credit: @John-Lin) 1.0.7 (2014-10-17) ------------------ **Bugfixes** - Fixed get_network_traffic method to return the pcap data (credit: adrianherrera) 1.0.6 (2014-09-22) ------------------ **Bugfixes** - Fixed a small typo in the private API's scan_file method (credit: adrianherrera) 1.0.5 (2014-05-18) ------------------ **Bugfixes** - Fixing README.rst for better PYPI presentation. 1.0.2 (2014-05-18) ------------------ **API Changes** - Changing folder structure so when people import it it is not dumb :( 1.0.1 (2014-04-14) ------------------ **Bugfixes** - Trying to fix setup.py for deploying to PYPI. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.6 virustotal-api-1.1.11/README.rst0000644000076600000240000001050513541733370017177 0ustar blacktopstaff00000000000000.. image:: https://raw.githubusercontent.com/blacktop/virustotal-api/master/doc/logo.png virustotal-api ============== .. image:: https://travis-ci.org/blacktop/virustotal-api.svg?branch=master :target: https://travis-ci.org/blacktop/virustotal-api .. image:: http://img.shields.io/:license-mit-blue.svg :target: http://doge.mit-license.org .. image:: https://img.shields.io/pypi/v/virustotal-api.svg :target: https://pypi.python.org/pypi/virustotal-api/ .. image:: https://img.shields.io/pypi/pyversions/virustotal-api.svg :target: https://pypi.python.org/pypi/virustotal-api/ Virus Total Public/Private/Intel API - https://www.virustotal.com/en/documentation/public-api/ - https://www.virustotal.com/en/documentation/private-api/ - https://www.virustotal.com/intelligence/help/automation/ Installation ------------ .. code-block:: bash $ pip install virustotal-api Usage ----- .. code-block:: python from __future__ import print_function import json import hashlib from virus_total_apis import PublicApi as VirusTotalPublicApi API_KEY = 'Sign-Up for API Key at virustotal.com' EICAR = "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".encode('utf-8') EICAR_MD5 = hashlib.md5(EICAR).hexdigest() vt = VirusTotalPublicApi(API_KEY) response = vt.get_file_report(EICAR_MD5) print(json.dumps(response, sort_keys=False, indent=4)) Output: ------- .. code-block:: json { "response_code": 200, "results": { "scan_id": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1397510237", "sha1": "3395856ce81f2b7382dee72602f798b642f14140", "resource": "44d88612fea8a8f36de82e1278abb02f", "response_code": 1, "scan_date": "2014-04-14 21:17:17", "permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1397510237/", "verbose_msg": "Scan finished, scan information embedded in this object", "sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f", "positives": 49, "total": 51, "md5": "44d88612fea8a8f36de82e1278abb02f", "scans": { "Bkav": { "detected": true, "version": "1.3.0.4959", "result": "DOS.EiracA.Trojan", "update": "20140412" }, "MicroWorld-eScan": { "detected": true, "version": "12.0.250.0", "result": "EICAR-Test-File", "update": "20140414" }, "nProtect": { "detected": true, "version": "2014-04-14.02", "result": "EICAR-Test-File", "update": "20140414" }, ...... "AVG": { "detected": true, "version": "13.0.0.3169", "result": "EICAR_Test", "update": "20140414" }, "Panda": { "detected": true, "version": "10.0.3.5", "result": "EICAR-AV-TEST-FILE", "update": "20140414" }, "Qihoo-360": { "detected": true, "version": "1.0.0.1015", "result": "Trojan.Generic", "update": "20140414" } } } } Testing ------- To run the tests: .. code-block:: bash $ ./tests Documentation ------------- You're looking at it. Issues ------ Find a bug? Want more features? Find something missing in the documentation? Let me know! Please don't hesitate to `file an issue `_ and I'll get right on it. Contributing ------------ `See all contributors on GitHub `_. Please update the `HISTORY.rst `_, and submit a `Pull Request on GitHub `_. License ------- MIT Copyright (c) 2014-2019 **blacktop** virustotal-api-1.1.11/setup.cfg0000644000076600000240000000007513541733575017341 0ustar blacktopstaff00000000000000[wheel] universal = 1 [egg_info] tag_build = tag_date = 0 virustotal-api-1.1.11/setup.py0000644000076600000240000000253113541733446017226 0ustar blacktopstaff00000000000000#!/usr/bin/env python import os import sys from codecs import open from setuptools import setup import virus_total_apis if sys.argv[-1] == 'publish': os.system('python setup.py sdist upload') sys.exit() version = virus_total_apis.__version__ if not version: raise RuntimeError('Cannot find version information') with open('README.rst', 'r', 'utf-8') as f: readme = f.read() with open('HISTORY.rst', 'r', 'utf-8') as f: history = f.read() setup( name='virustotal-api', version=virus_total_apis.__version__, description='Virus Total Public/Private/Intel API', long_description=readme + '\n\n' + history, url='https://github.com/blacktop/virustotal-api', author='blacktop', author_email='dev@blacktop.io', license=virus_total_apis.__license__, zip_safe=False, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.6', ], test_suite="tests", packages=['virus_total_apis'], package_data={'': ['LICENSE', 'NOTICE']}, package_dir={'virus_total_apis': 'virus_total_apis'}, include_package_data=True, install_requires=["requests >= 2.22.0"]) virustotal-api-1.1.11/virus_total_apis/0000755000076600000240000000000013541733575021105 5ustar blacktopstaff00000000000000virustotal-api-1.1.11/virus_total_apis/__init__.py0000644000076600000240000000050413541732660023207 0ustar blacktopstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- __title__ = 'virustotal-api' __version__ = '1.1.11' __author__ = 'Josh Maine' __license__ = 'MIT' __copyright__ = 'Copyright (C) 2014-2017 Josh "blacktop" Maine' try: import requests except ImportError: pass from .api import ApiError, IntelApi, PrivateApi, PublicApi virustotal-api-1.1.11/virus_total_apis/api.py0000644000076600000240000014204213541732424022223 0ustar blacktopstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ Simple class to interact with VirusTotal's Public and Private API as well as VirusTotal Intelligence. :copyright: (c) 2014 by Josh "blacktop" Maine. :license: MIT, see LICENSE for more details. The APIs are documented at: https://www.virustotal.com/en/documentation/public-api/ https://www.virustotal.com/en/documentation/private-api/ https://www.virustotal.com/intelligence/help/automation/ EXAMPLE USAGE::: from virus_total_apis import PublicApi as vtPubAPI vt = vtPubAPI() response = vt.get_file_report('44cda81782dc2a346abd7b2285530c5f') print json.dumps(response, sort_keys=False, indent=4) """ import json import os from datetime import datetime, timedelta try: import requests except ImportError: pass class PublicApi(): """ VirusTotal's Public API lets you upload and scan files, submit and scan URLs, access finished scan reports and make automatic comments on URLs and samples without the need of using the HTML website interface. In other words, it allows you to build simple scripts to access the information generated by VirusTotal. The chosen format for the API is HTTP POST requests with JSON object responses and it is limited to at most 4 requests of any nature in any given 1 minute time frame. If you run a honeyclient, honeypot or any other automation that is going to provide resources to VirusTotal and not only retrieve reports you are entitled to a higher request rate quota, ask for it at contact@virustotal.com and you will receive special privileges when performing the calls to the API. Note that you will only have a higher request rate quota when asking for files or URLs that you previously sent to VirusTotal. In this second version we have improved the response format so as to ease the task of retrieving results, we have also introduced batch requests, you may now ask for several items with a sole API call (as long as you cohere with the request rate limit). The public API is a free service, available for any website or application that is free to consumers. The API must not be used in commercial products or services, it can not be used as a substitute for antivirus products and it can not be integrated in any project that may harm the antivirus industry directly or indirectly. Noncompliance of these terms will result in inmediate permanent ban of the infractor individual or organization. """ def __init__(self, api_key=None, proxies=None): self.api_key = api_key self.proxies = proxies self.base = 'https://www.virustotal.com/vtapi/v2/' self.version = 2 if api_key is None: raise ApiError("You must supply a valid VirusTotal API key.") def scan_file(self, this_file, from_disk=True, filename=None, timeout=None): """ Submit a file to be scanned by VirusTotal. The VirusTotal API allows you to send files. Before performing your submissions we encourage you to retrieve the latest report on the files, if it is recent enough you might want to save time and bandwidth by making use of it. File size limit is 32MB. If you have a need to scan larger files, please contact us, and tell us your use case. :param this_file: The file to be uploaded. (32MB file size limit) :param from_disk: If True we read the file contents from disk using this_file as filepath. If False this_file is the actual file object. :param filename: Specify the filename, this overwrites the filename if we read a file from disk. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response that contains scan_id and permalink. """ params = {'apikey': self.api_key} if from_disk: if not filename: filename = os.path.basename(this_file) files = {'file': (filename, open(this_file, 'rb').read())} else: if filename: files = {'file': (filename, this_file)} else: files = {'file': this_file} try: response = requests.post( self.base + 'file/scan', files=files, params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def rescan_file(self, this_hash, timeout=None): """ Rescan a previously submitted filed or schedule an scan to be performed in the future. :param this_hash: a md5/sha1/sha256 hash. You can also specify a CSV list made up of a combination of any of the three allowed hashes (up to 25 items), this allows you to perform a batch request with one single call. Note that the file must already be present in our file store. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response that contains scan_id and permalink. """ params = {'apikey': self.api_key, 'resource': this_hash} try: response = requests.post(self.base + 'file/rescan', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file_report(self, hash_or_list, timeout=None): """ Get the scan results for a file. You can also specify a CSV list made up of a combination of hashes and scan_ids (up to 4 items with the standard request rate), this allows you to perform a batch request with one single call. i.e. {'resource': '99017f6eebbac24f351415dd410d522d, 88817f6eebbac24f351415dd410d522d'}. :param this_hash: The md5/sha1/sha256/scan_ids hash of the file whose dynamic behavioural report you want to retrieve or scan_ids from a previous call to scan_file or a list of hashes. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response that contains files report(s) """ if isinstance(hash_or_list, list): hash_or_list = ", ".join(hash_or_list) params = {'apikey': self.api_key, 'resource': hash_or_list} try: response = requests.get(self.base + 'file/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def scan_url(self, this_url, timeout=None): """ Submit a URL to be scanned by VirusTotal. Allows you to submit URLs to be scanned by VirusTotal. Before performing your submission we encourage you to retrieve the latest report on the URL, if it is recent enough you might want to save time and bandwidth by making use of it. :param this_url: The URL that should be scanned. This parameter accepts a list of URLs (up to 4 with the standard request rate) so as to perform a batch scanning request with one single call. The URLs must be separated by a new line character. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response that contains scan_id and permalink. """ params = {'apikey': self.api_key, 'url': this_url} try: response = requests.post(self.base + 'url/scan', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_url_report(self, this_url, scan='0', timeout=None): """ Get the scan results for a URL. (can do batch searches like get_file_report) :param this_url: a URL will retrieve the most recent report on the given URL. You may also specify a scan_id (sha256-timestamp as returned by the URL submission API) to access a specific report. At the same time, you can specify a CSV list made up of a combination of hashes and scan_ids so as to perform a batch request with one single call (up to 4 resources per call with the standard request rate). When sending multiples, the scan_ids or URLs must be separated by a new line character. :param scan: (optional): this is an optional parameter that when set to "1" will automatically submit the URL for analysis if no report is found for it in VirusTotal's database. In this case the result will contain a scan_id field that can be used to query the analysis report later on. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'resource': this_url, 'scan': scan} try: response = requests.get(self.base + 'url/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def put_comments(self, resource, comment, timeout=None): """ Post a comment on a file or URL. The initial idea of VirusTotal Community was that users should be able to make comments on files and URLs, the comments may be malware analyses, false positive flags, disinfection instructions, etc. Imagine you have some automatic setup that can produce interesting results related to a given sample or URL that you submit to VirusTotal for antivirus characterization, you might want to give visibility to your setup by automatically reviewing samples and URLs with the output of your automation. :param resource: either a md5/sha1/sha256 hash of the file you want to review or the URL itself that you want to comment on. :param comment: the actual review, you can tag it using the "#" twitter-like syntax (e.g. #disinfection #zbot) and reference users using the "@" syntax (e.g. @VirusTotalTeam). :param timeout: The amount of time in seconds the request should wait before timing out. :return: If the comment was successfully posted the response code will be 1, 0 otherwise. """ params = {'apikey': self.api_key, 'resource': resource, 'comment': comment} try: response = requests.post(self.base + 'comments/put', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_ip_report(self, this_ip, timeout=None): """ Get IP address reports. :param this_ip: a valid IPv4 address in dotted quad notation, for the time being only IPv4 addresses are supported. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'ip': this_ip} try: response = requests.get( self.base + 'ip-address/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_domain_report(self, this_domain, timeout=None): """ Get information about a given domain. :param this_domain: a domain name. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'domain': this_domain} try: response = requests.get(self.base + 'domain/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) class PrivateApi(PublicApi): def scan_file(self, this_file, notify_url=None, notify_changes_only=None, from_disk=True, filename=None, timeout=None): """ Submit a file to be scanned by VirusTotal. Allows you to send a file for scanning with VirusTotal. Before performing your submissions we encourage you to retrieve the latest report on the files, if it is recent enough you might want to save time and bandwidth by making use of it. File size limit is 32MB, in order to submmit files up to 200MB in size you must request a special upload URL. :param this_file: The file to be uploaded. :param notify_url: A URL to which a POST notification should be sent when the scan finishes. :param notify_changes_only: Used in conjunction with notify_url. Indicates if POST notifications should be sent only if the scan results differ from the previous analysis. :param from_disk: If True we read the file contents from disk using this_file as filepath. If False this_file is the actual file object. :param filename: Specify the filename, this overwrites the filename if we read a file from disk. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response that contains scan_id and permalink. """ params = {'apikey': self.api_key} if from_disk: if not filename: filename = os.path.basename(this_file) files = {'file': (filename, open(this_file, 'rb').read())} else: if filename: files = {'file': (filename, this_file)} else: files = {'file': this_file} try: response = requests.post( self.base + 'file/scan', files=files, params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) @property def get_upload_url(self, timeout=None): """ Get a special URL for submitted files bigger than 32MB. In order to submit files bigger than 32MB you need to obtain a special upload URL to which you can POST files up to 200MB in size. This API generates such a URL. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON special upload URL to which you can POST files up to 200MB in size. """ params = {'apikey': self.api_key} try: response = requests.get( self.base + 'file/scan/upload_url', params=params, proxies=self.proxies, timeout=timeout) if response.status_code == requests.codes.ok: return response.json().get('upload_url') else: return dict(response_code=response.status_code) except requests.RequestException as e: return dict(error=str(e)) def rescan_file(self, resource, date='', period='', repeat='', notify_url='', notify_changes_only='', timeout=None): """ Rescan a previously submitted filed or schedule an scan to be performed in the future. This API allows you to rescan files present in VirusTotal's file store without having to resubmit them, thus saving bandwidth. You only need to know one of the hashes of the file to rescan. :param resource: An md5/sha1/sha256 hash. You can also specify a CSV list made up of a combination of any of the three allowed hashes (up to 25 items), this allows you to perform a batch request with just one single call. Note that the file must already be present in our file store. :param date: (optional) Date in %Y%m%d%H%M%S format (example: 20120725170000) in which the rescan should be performed. If not specified the rescan will be performed immediately. :param period: (optional) Periodicity (in days) with which the file should be rescanned. If this argument is provided the file will be rescanned periodically every period days, if not, the rescan is performed once and not repated again. :param repeat: (optional) Used in conjunction with period to specify the number of times the file should be rescanned. If this argument is provided the file will be rescanned the given amount of times in coherence with the chosen periodicity, if not, the file will be rescanned indefinitely. :param notify_url: (optional) A URL to which a POST notification should be sent when the rescan finishes. :param notify_changes_only: (optional) Used in conjunction with notify_url. Indicates if POST notifications should only be sent if the scan results differ from the previous one. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response that contains scan_id and permalink. """ params = {'apikey': self.api_key, 'resource': resource} try: response = requests.post(self.base + 'file/rescan', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def cancel_rescan_file(self, resource, timeout=None): """ Delete a previously scheduled scan. Deletes a scheduled file rescan task. The file rescan api allows you to schedule periodic scans of a file, this API call tells VirusTotal to stop rescanning a file that you have previously enqueued for recurrent scanning. :param resource: The md5/sha1/sha256 hash of the file whose dynamic behavioural report you want to retrieve. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON acknowledgement. In the event that the scheduled scan deletion fails for whatever reason, the response code will be -1. """ params = {'apikey': self.api_key, 'resource': resource} try: response = requests.post(self.base + 'rescan/delete', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file_report(self, resource, allinfo=1, timeout=None): """ Get the scan results for a file. Retrieves a concluded file scan report for a given file. Unlike the public API, this call allows you to also access all the information we have on a particular file (VirusTotal metadata, signature information, structural information, etc.) by using the allinfo parameter described later on. :param resource: An md5/sha1/sha256 hash of a file for which you want to retrieve the most recent antivirus report. You may also specify a scan_id (sha256-timestamp as returned by the scan API) to access a specific report. You can also specify a CSV list made up of a combination of hashes and scan_ids (up to 25 items), this allows you to perform a batch request with just one single call. :param allinfo: (optional) If specified and set to one, the call will return additional info, other than the antivirus results, on the file being queried. This additional info includes the output of several tools acting on the file (PDFiD, ExifTool, sigcheck, TrID, etc.), metadata regarding VirusTotal submissions (number of unique sources that have sent the file in the past, first seen date, last seen date, etc.), the output of in-house technologies such as a behavioural sandbox, etc. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'resource': resource, 'allinfo': allinfo} try: response = requests.get(self.base + 'file/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file_behaviour(self, this_hash, timeout=None): """ Get a report about the behaviour of the file in sand boxed environment. VirusTotal runs a distributed setup of Cuckoo sandbox machines that execute the files we receive. Execution is attempted only once, upon first submission to VirusTotal, and only Portable Executables under 10MB in size are ran. The execution of files is a best effort process, hence, there are no guarantees about a report being generated for a given file in our dataset. If a file did indeed produce a behavioural report, a summary of it can be obtained by using the file scan lookup call providing the additional HTTP POST parameter allinfo=1. The summary will appear under the behaviour-v1 property of the additional_info field in the JSON report. :param this_hash: The md5/sha1/sha256 hash of the file whose dynamic behavioural report you want to retrieve. :param timeout: The amount of time in seconds the request should wait before timing out. :return: full JSON report of the file's execution as returned by the Cuckoo JSON report encoder. """ params = {'apikey': self.api_key, 'hash': this_hash} try: response = requests.get(self.base + 'file/behaviour', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_network_traffic(self, this_hash, timeout=None): """ Get a dump of the network traffic generated by the file. VirusTotal runs a distributed setup of Cuckoo sandbox machines that execute the files we receive. Execution is attempted only once, upon first submission to VirusTotal, and only Portable Executables under 10MB in size are ran. The execution of files is a best effort process, hence, there are no guarantees about a report being generated for a given file in our dataset. Files that are successfully executed may communicate with certain network resources, all this communication is recorded in a network traffic dump (pcap file). This API allows you to retrieve the network traffic dump generated during the file's execution. :param this_hash: The md5/sha1/sha256 hash of the file whose network traffic dump you want to retrieve. :param timeout: The amount of time in seconds the request should wait before timing out :return: Pcap """ params = {'apikey': self.api_key, 'hash': this_hash} try: response = requests.get( self.base + 'file/network-traffic', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) try: return _return_response_and_status_code(response) except ValueError: return response.content def file_search(self, query, offset=None, timeout=None): """ Search for samples. In addition to retrieving all information on a particular file, VirusTotal allows you to perform what we call "advanced reverse searches". Reverse searches take you from a file property to a list of files that match that property. For example, this functionality enables you to retrieve all those files marked by at least one antivirus vendor as Zbot, or all those files that have a size under 90KB and are detected by at least 10 antivirus solutions, or all those PDF files that have an invalid XREF section, etc. This API is equivalent to VirusTotal Intelligence advanced searches. A very wide variety of search modifiers are available, including: file size, file type, first submission date to VirusTotal, last submission date to VirusTotal, number of positives, dynamic behavioural properties, binary content, submission file name, and a very long etcetera. The full list of search modifiers allowed for file search queries is documented at: https://www.virustotal.com/intelligence/help/file-search/#search-modifiers NOTE: Daily limited! No matter what API step you have licensed, this API call is limited to 50K requests per day. If you need any more, chances are you are approaching your engineering problem erroneously and you can probably solve it using the file distribution call. Do not hesitate to contact us with your particular use case. EXAMPLE: search_options = 'type:peexe size:90kb+ positives:5+ behaviour:"taskkill"' :param query: A search modifier compliant file search query. :param offset: (optional) The offset value returned by a previously issued identical query, allows you to paginate over the results. If not specified the first 300 matching files sorted according to last submission date to VirusTotal in a descending fashion will be returned. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response - By default the list returned contains at most 300 hashes, ordered according to last submission date to VirusTotal in a descending fashion. """ params = dict(apikey=self.api_key, query=query, offset=offset) try: response = requests.get(self.base + 'file/search', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file_clusters(self, this_date, timeout=None): """ File similarity clusters for a given time frame. VirusTotal has built its own in-house file similarity clustering functionality. At present, this clustering works only on PE, PDF, DOC and RTF files and is based on a very simple structural feature hash. This hash can very often be confused by certain compression and packing strategies, in other words, this clustering logic is no holly grail, yet it has proven itself very useful in the past. This API offers a programmatic access to the clustering section of VirusTotal Intelligence: https://www.virustotal.com/intelligence/clustering/ NOTE: Please note that you must be logged in with a valid VirusTotal Community user account with access to VirusTotal Intelligence in order to be able to view the clustering listing. :param this_date: A specific day for which we want to access the clustering details, example: 2013-09-10. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON object contains several properties num_candidates - Total number of files submitted during the given time frame for which a feature hash could be calculated. num_clusters - Total number of clusters generated for the given time period under consideration, a cluster can be as small as an individual file, meaning that no other feature-wise similar file was found. size_top200 - The sum of the number of files in the 200 largest clusters identified. clusters - List of JSON objects that contain details about the 200 largest clusters identified. These objects contain 4 properties: id, label, size and avg_positives.. The id field can be used to then query the search API call for files contained in the given cluster. The label property is a verbose human-intelligible name for the cluster. The size field is the number of files that make up the cluster. Finally, avg_positives represents the average number of antivirus detections that the files in the cluster exhibit. """ params = {'apikey': self.api_key, 'date': this_date} try: response = requests.get(self.base + 'file/clusters', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file_distribution(self, before='', after='', reports='false', limit='1000', timeout=None): """ Get a live feed with the latest files submitted to VirusTotal. Allows you to retrieve a live feed of absolutely all uploaded files to VirusTotal, and download them for further scrutiny. This API requires you to stay synced with the live submissions as only a backlog of 6 hours is provided at any given point in time. :param before: (optional) Retrieve files received before the given timestamp, in timestamp descending order. :param after: (optional) Retrieve files received after the given timestamp, in timestamp ascending order. :param reports: (optional) Include the files' antivirus results in the response. Possible values are 'true' or 'false' (default value is 'false'). :param limit: (optional) Retrieve limit file items at most (default: 1000). :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response: please see https://www.virustotal.com/en/documentation/private-api/#file-distribution """ params = {'apikey': self.api_key, 'before': before, 'after': after, 'reports': reports, 'limit': limit} try: response = requests.get( self.base + 'file/distribution', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file_feed(self, package=None, timeout=None): """ Get a live file feed with the latest files submitted to VirusTotal. Allows you to retrieve a live feed of absolutely all uploaded files to VirusTotal, and download them for further scrutiny, along with their full reports. This API requires you to stay relatively synced with the live submissions as only a backlog of 24 hours is provided at any given point in time. This API returns a bzip2 compressed tarball. For per-minute packages the compressed package contains a unique file, the file contains a json per line, this json is a full report on a given file processed by VirusTotal during the given time window. The file report follows the exact same format as the response of the file report API if the allinfo=1 parameter is provided. For hourly packages, the tarball contains 60 files, one per each minute of the window. :param package: Indicates a time window to pull reports on all items received during such window. Only per-minute and hourly windows are allowed, the format is %Y%m%dT%H%M (e.g. 20160304T0900) or %Y%m%dT%H (e.g. 20160304T09). Time is expressed in UTC. :param timeout: The amount of time in seconds the request should wait before timing out. :return: BZIP2 response: please see https://www.virustotal.com/en/documentation/private-api/#file-feed """ if package is None: now = datetime.utcnow() five_minutes_ago = now - timedelta( minutes=now.minute % 5 + 5, seconds=now.second, microseconds=now.microsecond) package = five_minutes_ago.strftime('%Y%m%dT%H%M') params = {'apikey': self.api_key, 'package': package} try: response = requests.get(self.base + 'file/feed', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response, json_results=False) def get_file(self, this_hash, timeout=None): """ Download a file by its hash. Downloads a file from VirusTotal's store given one of its hashes. This call can be used in conjuction with the file searching call in order to download samples that match a given set of criteria. :param this_hash: The md5/sha1/sha256 hash of the file you want to download. :param timeout: The amount of time in seconds the request should wait before timing out. :return: Downloaded file in response.content """ params = {'apikey': self.api_key, 'hash': this_hash} try: response = requests.get(self.base + 'file/download', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response, json_results=False) def get_url_report(self, this_url, scan='0', allinfo=1, timeout=None): """ Get the scan results for a URL. :param this_url: A URL for which you want to retrieve the most recent report. You may also specify a scan_id (sha256-timestamp as returned by the URL submission API) to access a specific report. At the same time, you can specify a CSV list made up of a combination of urls and scan_ids (up to 25 items) so as to perform a batch request with one single call. The CSV list must be separated by new line characters. :param scan: (optional) This is an optional parameter that when set to "1" will automatically submit the URL for analysis if no report is found for it in VirusTotal's database. In this case the result will contain a scan_id field that can be used to query the analysis report later on. :param allinfo: (optional) If this parameter is specified and set to "1" additional info regarding the URL (other than the URL scanning engine results) will also be returned. This additional info includes VirusTotal related metadata (first seen date, last seen date, files downloaded from the given URL, etc.) and the output of other tools and datasets when fed with the URL. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'resource': this_url, 'scan': scan, 'allinfo': allinfo} try: response = requests.get(self.base + 'url/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_url_distribution(self, after=None, reports='true', limit=1000, timeout=None): """ Get a live feed with the lastest URLs submitted to VirusTotal. Allows you to retrieve a live feed of URLs submitted to VirusTotal, along with their scan reports. This call enables you to stay synced with VirusTotal URL submissions and replicate our dataset. :param after: (optional) Retrieve URLs received after the given timestamp, in timestamp ascending order. :param reports: (optional) When set to "true" each item retrieved will include the results for each particular URL scan (in exactly the same format as the URL scan retrieving API). If the parameter is not specified, each item returned will only contain the scanned URL and its detection ratio. :param limit: (optional) Retrieve limit file items at most (default: 1000). :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'after': after, 'reports': reports, 'limit': limit} try: response = requests.get( self.base + 'url/distribution', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_url_feed(self, package=None, timeout=None): """ Get a live file feed with the latest files submitted to VirusTotal. Allows you to retrieve a live feed of reports on absolutely all URLs scanned by VirusTotal. This API requires you to stay relatively synced with the live submissions as only a backlog of 24 hours is provided at any given point in time. This API returns a bzip2 compressed tarball. For per-minute packages the compressed package contains a unique file, the file contains a json per line, this json is a full report on a given URL processed by VirusTotal during the given time window. The URL report follows the exact same format as the response of the URL report API if the allinfo=1 parameter is provided. For hourly packages, the tarball contains 60 files, one per each minute of the window. :param package: Indicates a time window to pull reports on all items received during such window. Only per-minute and hourly windows are allowed, the format is %Y%m%dT%H%M (e.g. 20160304T0900) or %Y%m%dT%H (e.g. 20160304T09). Time is expressed in UTC. :param timeout: The amount of time in seconds the request should wait before timing out. :return: BZIP2 response: please see https://www.virustotal.com/en/documentation/private-api/#file-feed """ if package is None: now = datetime.utcnow() five_minutes_ago = now - timedelta( minutes=now.minute % 5 + 5, seconds=now.second, microseconds=now.microsecond) package = five_minutes_ago.strftime('%Y%m%dT%H%M') params = {'apikey': self.api_key, 'package': package} try: response = requests.get(self.base + 'url/feed', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response, json_results=False) def get_ip_report(self, this_ip, timeout=None): """ Get information about a given IP address. Retrieves a report on a given IP address (including the information recorded by VirusTotal's Passive DNS infrastructure). :param this_ip: A valid IPv4 address in dotted quad notation, for the time being only IPv4 addresses are supported. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'ip': this_ip} try: response = requests.get( self.base + 'ip-address/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_domain_report(self, this_domain, timeout=None): """ Get information about a given domain. Retrieves a report on a given domain (including the information recorded by VirusTotal's passive DNS infrastructure). :param this_domain: A domain name. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response """ params = {'apikey': self.api_key, 'domain': this_domain} try: response = requests.get(self.base + 'domain/report', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_comments(self, resource, before=None, timeout=None): """ Get comments for a file or URL. Retrieve a list of VirusTotal Community comments for a given file or URL. VirusTotal Community comments are user submitted reviews on a given item, these comments may contain anything from the in-the-wild locations of files up to fully-featured reverse engineering reports on a given sample. :param resource: Either an md5/sha1/sha256 hash of the file or the URL itself you want to retrieve. :param before: (optional) A datetime token that allows you to iterate over all comments on a specific item whenever it has been commented on more than 25 times. :param timeout: The amount of time in seconds the request should wait before timing out. :return: JSON response - The application answers with the comments sorted in descending order according to their date. """ params = dict(apikey=self.api_key, resource=resource, before=before) try: response = requests.get(self.base + 'comments/get', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) class IntelApi(): """ To make the best use of your VirusTotal Intelligence account and so, we have exposed some VirusTotal Intelligence functionality for programmatic interaction even if you do not have a Private Mass API key. """ def __init__(self, api_key, proxies=None): self.api_key = api_key self.proxies = proxies self.base = 'https://www.virustotal.com/intelligence/' def get_hashes_from_search(self, query, page=None, timeout=None): """ Get the scan results for a file. Even if you do not have a Private Mass API key that you can use, you can still automate VirusTotal Intelligence searches pretty much in the same way that the searching for files api call works. :param query: a VirusTotal Intelligence search string in accordance with the file search documentation . :param page: the next_page property of the results of a previously issued query to this API. This parameter should not be provided if it is the very first query to the API, i.e. if we are retrieving the first page of results. :param timeout: The amount of time in seconds the request should wait before timing out. apikey: the API key associated to a VirusTotal Community account with VirusTotal Intelligence privileges. """ params = {'query': query, 'apikey': self.api_key, 'page': page} try: response = requests.get( self.base + 'search/programmatic/', params=params, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def get_file(self, file_hash, save_file_at, timeout=None): """ Get the scan results for a file. Even if you do not have a Private Mass API key that you can use, you can still download files from the VirusTotal storage making use of your VirusTotal Intelligence quota, i.e. programmatic downloads will also deduct quota. :param file_hash: You may use either the md5, sha1 or sha256 hash of the file in order to download it. :param save_file_at: Path of where to save the file. :param timeout: The amount of time in seconds the request should wait before timing out. """ params = {'hash': file_hash, 'apikey': self.api_key} try: response = requests.get( self.base + 'download/', params=params, proxies=self.proxies, stream=True, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) if response.status_code == requests.codes.ok: self.save_downloaded_file(file_hash, save_file_at, response.content) return _return_response_and_status_code(response, json_results=False) def get_all_file_report_pages(self, query): """ Get File Report (All Pages). :param query: a VirusTotal Intelligence search string in accordance with the file search documentation. :return: All JSON responses appended together. """ responses = [] r = self.get_hashes_from_search(query) responses.append(r) if ('results' in r.keys()) and ('next_page' in r['results'].keys()): next_page = r['results']['next_page'] else: next_page = None while next_page: r = self.get_hashes_from_search(query, next_page) if ('results' in r.keys()) and ('next_page' in r['results'].keys()): next_page = r['results']['next_page'] else: next_page = None responses.append(r) return dict(results=responses) def get_intel_notifications_feed(self, page=None, timeout=None): """ Get notification feed in JSON for further processing. :param page: the next_page property of the results of a previously issued query to this API. This parameter should not be provided if it is the very first query to the API, i.e. if we are retrieving the first page of results. :param timeout: The amount of time in seconds the request should wait before timing out. :returns: The next page identifier, The results (JSON is possible with .json()) """ params = {'apikey': self.api_key, 'next': page} try: response = requests.get( self.base + 'hunting/notifications-feed/', params=params, proxies=self.proxies, timeout=timeout) # VT returns an empty result, len(content)==0, and status OK if there are no pending notifications. # To keep the API consistent we generate an empty object instead. # This might not be necessary with a later release of the VTI API. (bug has been submitted) if len(response.content) == 0: response.__dict__['_content'] = \ b'{"notifications":[],"verbose_msg":"No pending notification","result":0,"next":null}' except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) def delete_intel_notifications(self, ids, timeout=None): """ Programmatically delete notifications via the Intel API. :param ids: A list of IDs to delete from the notification feed. :returns: The post response. """ if not isinstance(ids, list): raise TypeError("ids must be a list") # VirusTotal needs ids as a stringified array data = json.dumps(ids) try: response = requests.post( self.base + 'hunting/delete-notifications/programmatic/?key=' + self.api_key, data=data, proxies=self.proxies, timeout=timeout) except requests.RequestException as e: return dict(error=str(e)) return _return_response_and_status_code(response) @staticmethod def save_downloaded_file(filename, save_file_at, file_stream): """ Save Downloaded File to Disk Helper Function :param save_file_at: Path of where to save the file. :param file_stream: File stream :param filename: Name to save the file. """ filename = os.path.join(save_file_at, filename) with open(filename, 'wb') as f: f.write(file_stream) f.flush() class ApiError(Exception): pass def _return_response_and_status_code(response, json_results=True): """ Output the requests response content or content as json and status code :rtype : dict :param response: requests response object :param json_results: Should return JSON or raw content :return: dict containing the response content and/or the status code with error string. """ if response.status_code == requests.codes.ok: return dict(results=response.json() if json_results else response.content, response_code=response.status_code) elif response.status_code == 400: return dict( error='package sent is either malformed or not within the past 24 hours.', response_code=response.status_code) elif response.status_code == 204: return dict( error='You exceeded the public API request rate limit (4 requests of any nature per minute)', response_code=response.status_code) elif response.status_code == 403: return dict( error='You tried to perform calls to functions for which you require a Private API key.', response_code=response.status_code) elif response.status_code == 404: return dict(error='File not found.', response_code=response.status_code) else: return dict(response_code=response.status_code) virustotal-api-1.1.11/virustotal_api.egg-info/0000755000076600000240000000000013541733575022255 5ustar blacktopstaff00000000000000virustotal-api-1.1.11/virustotal_api.egg-info/PKG-INFO0000644000076600000240000002267413541733575023365 0ustar blacktopstaff00000000000000Metadata-Version: 1.1 Name: virustotal-api Version: 1.1.11 Summary: Virus Total Public/Private/Intel API Home-page: https://github.com/blacktop/virustotal-api Author: blacktop Author-email: dev@blacktop.io License: MIT Description: .. image:: https://raw.githubusercontent.com/blacktop/virustotal-api/master/doc/logo.png virustotal-api ============== .. image:: https://travis-ci.org/blacktop/virustotal-api.svg?branch=master :target: https://travis-ci.org/blacktop/virustotal-api .. image:: http://img.shields.io/:license-mit-blue.svg :target: http://doge.mit-license.org .. image:: https://img.shields.io/pypi/v/virustotal-api.svg :target: https://pypi.python.org/pypi/virustotal-api/ .. image:: https://img.shields.io/pypi/pyversions/virustotal-api.svg :target: https://pypi.python.org/pypi/virustotal-api/ Virus Total Public/Private/Intel API - https://www.virustotal.com/en/documentation/public-api/ - https://www.virustotal.com/en/documentation/private-api/ - https://www.virustotal.com/intelligence/help/automation/ Installation ------------ .. code-block:: bash $ pip install virustotal-api Usage ----- .. code-block:: python from __future__ import print_function import json import hashlib from virus_total_apis import PublicApi as VirusTotalPublicApi API_KEY = 'Sign-Up for API Key at virustotal.com' EICAR = "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".encode('utf-8') EICAR_MD5 = hashlib.md5(EICAR).hexdigest() vt = VirusTotalPublicApi(API_KEY) response = vt.get_file_report(EICAR_MD5) print(json.dumps(response, sort_keys=False, indent=4)) Output: ------- .. code-block:: json { "response_code": 200, "results": { "scan_id": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1397510237", "sha1": "3395856ce81f2b7382dee72602f798b642f14140", "resource": "44d88612fea8a8f36de82e1278abb02f", "response_code": 1, "scan_date": "2014-04-14 21:17:17", "permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1397510237/", "verbose_msg": "Scan finished, scan information embedded in this object", "sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f", "positives": 49, "total": 51, "md5": "44d88612fea8a8f36de82e1278abb02f", "scans": { "Bkav": { "detected": true, "version": "1.3.0.4959", "result": "DOS.EiracA.Trojan", "update": "20140412" }, "MicroWorld-eScan": { "detected": true, "version": "12.0.250.0", "result": "EICAR-Test-File", "update": "20140414" }, "nProtect": { "detected": true, "version": "2014-04-14.02", "result": "EICAR-Test-File", "update": "20140414" }, ...... "AVG": { "detected": true, "version": "13.0.0.3169", "result": "EICAR_Test", "update": "20140414" }, "Panda": { "detected": true, "version": "10.0.3.5", "result": "EICAR-AV-TEST-FILE", "update": "20140414" }, "Qihoo-360": { "detected": true, "version": "1.0.0.1015", "result": "Trojan.Generic", "update": "20140414" } } } } Testing ------- To run the tests: .. code-block:: bash $ ./tests Documentation ------------- You're looking at it. Issues ------ Find a bug? Want more features? Find something missing in the documentation? Let me know! Please don't hesitate to `file an issue `_ and I'll get right on it. Contributing ------------ `See all contributors on GitHub `_. Please update the `HISTORY.rst `_, and submit a `Pull Request on GitHub `_. License ------- MIT Copyright (c) 2014-2019 **blacktop** .. :changelog: Release History --------------- 1.1.11 (2019-09-22) ------------------- **Allow for hash list input in get_file_report** - https://github.com/blacktop/virustotal-api/pull/28 (credit: @CDuPlooy) 1.1.10 (2018-03-12) ------------------- **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/23 (credit: @leadZERO) 1.1.9 (2018-01-03 aka the day the CPUs fell) -------------------------------------------- **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/22 (credit: @leadZERO) 1.1.7 (2017-05-28) ------------------ **Intel API Fix** - https://github.com/blacktop/virustotal-api/pull/18 (credit: @doug-the-guy) 1.1.6 (2017-05-14) ------------------ **Py3 Fix** - Change `e.message` to `str(message)` (credit: [@DeanF](https://github.com/blacktop/virustotal-api/pull/19)) 1.1.5 (2017-04-13) ------------------ **API Changes** - Added Intelligence notifications feed and ability to programmatically delete notifications from the feed. (credit: @keithjjones) 1.1.4 (2017-03-11) ------------------ **Fixed timeout functionality, removed unnecessary methods** - Fixed the timeout parameter in the PublicApi and removes unnecessary code in the PrivateApi (credit: @mrredamber aka LEGEND) 1.1.3 (2017-02-03) ------------------ **Request Timeout Functionality** - Adds a timeout parameter to methods that make requests to the VirusTotal API (credit: @mrredamber aka LEGEND) 1.1.2 (2016-04-13) ------------------ **API Changes** - Re-adding the ability to use files from memory as well as from disk. (credit: @tweemeterjop) 1.1.1 (2016-03-13) ------------------ **API Changes** - Adding file/url feed private API endpoint. 1.0.9 (2016-01-01) ------------------ **Privacyfixes** - Fix scan_file (upload to VT), do not leak full path. (credit: @Rafiot) 1.0.8 (2014-12-26) ------------------ **Bugfixes** - Fixed get_url_report method for the Private API (credit: @John-Lin) 1.0.7 (2014-10-17) ------------------ **Bugfixes** - Fixed get_network_traffic method to return the pcap data (credit: adrianherrera) 1.0.6 (2014-09-22) ------------------ **Bugfixes** - Fixed a small typo in the private API's scan_file method (credit: adrianherrera) 1.0.5 (2014-05-18) ------------------ **Bugfixes** - Fixing README.rst for better PYPI presentation. 1.0.2 (2014-05-18) ------------------ **API Changes** - Changing folder structure so when people import it it is not dumb :( 1.0.1 (2014-04-14) ------------------ **Bugfixes** - Trying to fix setup.py for deploying to PYPI. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.6 virustotal-api-1.1.11/virustotal_api.egg-info/SOURCES.txt0000644000076600000240000000053313541733575024142 0ustar blacktopstaff00000000000000HISTORY.rst LICENSE MANIFEST.in NOTICE README.rst setup.cfg setup.py virus_total_apis/__init__.py virus_total_apis/api.py virustotal_api.egg-info/PKG-INFO virustotal_api.egg-info/SOURCES.txt virustotal_api.egg-info/dependency_links.txt virustotal_api.egg-info/not-zip-safe virustotal_api.egg-info/requires.txt virustotal_api.egg-info/top_level.txtvirustotal-api-1.1.11/virustotal_api.egg-info/dependency_links.txt0000644000076600000240000000000113541733575026323 0ustar blacktopstaff00000000000000 virustotal-api-1.1.11/virustotal_api.egg-info/not-zip-safe0000644000076600000240000000000113074032530024463 0ustar blacktopstaff00000000000000 virustotal-api-1.1.11/virustotal_api.egg-info/requires.txt0000644000076600000240000000002113541733575024646 0ustar blacktopstaff00000000000000requests>=2.22.0 virustotal-api-1.1.11/virustotal_api.egg-info/top_level.txt0000644000076600000240000000002113541733575025000 0ustar blacktopstaff00000000000000virus_total_apis