Skip to content

Commit 05dfa8a

Browse files
authored
v0.1.0 - Library redesign (#24)
* Add v3 progress checklist. * Add EOF. Fix a spelling mistake. Add 0.1.0 changelog (Will probably add to this more later). * Bump description and version. * Add new parameter to file_scan. * Add username to LICENSE. * Fix license in file. Remove ImportError print. Add API_VERSION parameter and docstring to __init__ Adjust headers for each API version. Raise exception when API_VERSION is invalid. file_scan: Update docstring, added new parameter upload_url, added support for v3 API. Add new method file_upload_url. * Move comment up to beginning of if state for clarity. * Remove proxies from individual make_request calls. Add inside make_request. Add file_id method. add file_id_analyse method. * Remove checklist. * Add progress on re-write for v3 API support with backwards compatibility. See issue #23 * Move check on API_KEY before self.API_KEY. * Add link to v2 docs for error(). * Rename backwards_compatibility to COMPATIBILITY_ENABLED. Move to init method of Virustotal class. * Fix for issue where API_KEY is needed in query parameters. Might find something better soon. Fix mistake with BASEURL being BASE_URL. * Make requests response object @Property and fix naming causing AttributeError. * Add more @Property. * Remove truthy check. * Add VirustotalError exception class. Alter wording to retrieve. Add data and object_type properties. Usage with v3 API endpoints. Simplify response_code property. Replace json parameter with data in request(). Make method the last parameter in request(). Add docstring raises for validate_response(). * Add testing file to ignore. * Replace examples.py as oldexamples.py * Add some new examples. * Fix docstring :returns:. Fix return type hint. * Update packages in lock file. * Update requirements.txt with only nessasary packages. * Push progress on new README. * Add spacing to NOTES. * Add link to file in NOTE. * Push example with timeout and proxies. * Add examples to README. Add API key image. * Add word. * Add comment about MAX with v2 API. * Add useful comments to examples. * Add examples for providing an API key via an environment variable. * Add example about retrieving info about a domain. * Edit changelog. Remove WIP. * Add correct documentation for examples. * Add domain_info example. * Add start of comment.py example. * Add example for use with environment variable. * Alter file ID. A more interesting file. * Add json to request(). Fix docstring for data. Add docstring for json. * FIX: json.decoder.JSONDecodeError when user attempts to run json() and no JSON is present. * Alter to empty dict instead of None in case the user runs .data instead of .json(). * Add comments examples for v3 and v3. * Fix raises docstring. * Add graphs examples. * Add meta, cursor and links. * Add ip examples. * Add missing example for v2. * Add search and metadata endpoint examples. * Add example of using a cursor. * Progress on new tests. * Add progress on tests. * Add final tests. Remove skips. All tests pass. * Add section on how to run tests. * Add NOTE about how links are not retrieve from objects. * Alter word. * Add comment and catch for AttributeError which can occur. * Remove "as err" * Add missing class name. * Add correct variable for example. * Alter wording slightly. * Alter wording in docstring. * Alter wording slightly in sentence. * Fixes to some wording. Added some comments to examples. * Capitalise URL_ID. * Fix example variable. * Alter comments. * Alter wording. * Move comment down. * Edit example description. Remove repeated code. * Alter comment and print. * Alter example description. * Alter description. * Alter description again. * Alter description back. * Alter keywords and description. * Alter file description. * Remove fullstops. * Fix class docstring. * Fix docstrings. * Add example to comment. * Move v2 and v3 examples around. * Move v2 example above v3 example. * Add link to PR for 0.1.0. * Alter wording slightly in CHANGELOG for 0.1.0
1 parent 5446bdd commit 05dfa8a

19 files changed

Lines changed: 1512 additions & 415 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ wheels/
2424
.installed.cfg
2525
*.egg
2626
MANIFEST
27+
run.py
2728

2829
# PyInstaller
2930
# Usually these files are written by a python script from a template

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2020
3+
Copyright (c) 2020 dbrennand
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Pipfile.lock

Lines changed: 239 additions & 95 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 185 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
# virustotal-python 🐍
22
![PyPI](https://img.shields.io/pypi/v/virustotal-python.svg?style=flat-square)
33

4-
A light wrapper around the public VirusTotal API.
4+
A Python library to interact with the public VirusTotal v2 and v3 APIs.
55

6-
# Dependencies
7-
* Written in Python 3.7.
6+
> [!NOTE]
7+
>
8+
> This library is intended to be used with the public VirusTotal APIs. However, it *could* be used to interact with premium API endpoints as well.
9+
10+
# Dependencies and installation
11+
12+
> [!NOTE]
13+
>
14+
> This library should work with Python versions >= 3.7.
815
916
```
1017
[dev-packages]
@@ -19,105 +26,193 @@ requests = {extras = ["socks"],version = "*"}
1926
Install `virustotal-python` using either:
2027
* `pip3 install virustotal-python`, `pipenv install`, `pip3 install -r requirements.txt`, `python setup.py install`.
2128

22-
## Example Usage
29+
## Usage examples
30+
31+
> [!NOTE]
32+
>
33+
> See the [examples](examples) directory for several usage examples.
34+
>
35+
> Furthermore, check [`virustotal_python/virustotal.py`](virustotal_python/virustotal.py) for docstrings containing full parameter descriptions.
36+
37+
Authenticate using your VirusTotal API key:
38+
39+
> ![NOTE]
40+
>
41+
> To obtain a VirusTotal API key, [sign up](https://www.virustotal.com/gui/join-us) for a VirusTotal account.
42+
>
43+
> Then, view your VirusTotal API key.
44+
>
45+
> ![VirusTotal view API key](images/APIKey.png)
46+
2347
```python
2448
from virustotal_python import Virustotal
25-
from pprint import pprint
2649

27-
# Normal Initialisation.
28-
vtotal = Virustotal("Insert API Key Here.")
50+
# v2 example
51+
vtotal = Virustotal(API_KEY="Insert API key here.")
2952

30-
# NEW as of version 0.0.5: Proxy support.
31-
# Example Usage: Using HTTP(S)
32-
vtotal = Virustotal(
33-
"Insert API Key Here.",
34-
{"http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080"})
35-
# Or using SOCKS
53+
# v3 example
54+
vtotal = Virustotal(API_KEY="Insert API key here.", API_VERSION="v3")
55+
56+
# You can provide True to the `COMPATIBILITY_ENABLED` parameter to preserve the old response format of virustotal-python versions prior to 0.1.0
57+
vtotal = Virustotal(API_KEY="Insert API key here.", API_VERSION="v3", COMPATIBILITY_ENABLED=True)
58+
59+
# You can also set proxies and timeouts for requests made by the library
3660
vtotal = Virustotal(
37-
"Insert API Key Here.",
38-
{"http": "socks5://user:pass@host:port", "https": "socks5://user:pass@host:port"})
39-
40-
# NOTE: Check virustotal.py for docstrings containing full parameter descriptions.
41-
42-
# Send a file to Virustotal for analysis.
43-
resp = vtotal.file_scan("./tests.py") # PATH to file for querying.
44-
45-
# Retrieve scan report(s) for given file(s) from Virustotal.
46-
# A list containing the resource (SHA256) HASH of a known malicious file.
47-
resp = vtotal.file_report(
48-
["9f101483662fc071b7c10f81c64bb34491ca4a877191d464ff46fd94c7247115"]
49-
)
50-
# A list of resource(s). Can be `md5/sha1/sha256 hashes` and/or combination of hashes and scan_ids (MAX 4 per standard request rate).
51-
# The first is a scan_id, the second is a SHA256 HASH.
52-
resp = vtotal.file_report(
53-
[
54-
"75efd85cf6f8a962fe016787a7f57206ea9263086ee496fc62e3fc56734d4b53-1555351539",
55-
"9f101483662fc071b7c10f81c64bb34491ca4a877191d464ff46fd94c7247115",
56-
]
57-
)
58-
59-
# Query url(s) to VirusTotal.
60-
# A list containing a url to be scanned by VirusTotal.
61-
resp = vtotal.url_scan(["ihaveaproblem.info"]) # Query a single url.
62-
# A list of url(s) to be scanned by VirusTotal (MAX 4 per standard request rate).
63-
resp = vtotal.url_scan(
64-
["ihaveaproblem.info", "google.com", "wikipedia.com", "github.com"]
65-
)
66-
67-
# Retrieve url report(s)
68-
# A list containing the url of the report to be retrieved.
69-
resp = vtotal.url_report(["ihaveaproblem.info"]) # Query a single url.
70-
# A list of the url(s) and/or scan_id(s) report(s) to be retrieved (MAX 4 per standard request rate).
71-
# The first object in the list is a scan_id.
72-
resp = vtotal.url_report(
73-
[
74-
"fd21590d9df715452c8c000e1b5aa909c7c5ea434c2ddcad3f4ccfe9b0ee224e-1555352750",
75-
"google.com",
76-
"wikipedia.com",
77-
"github.com",
78-
],
79-
scan=1,
80-
)
81-
82-
# Query an IP to Virustotal.
83-
resp = vtotal.ipaddress_report("90.156.201.27")
84-
85-
# Retrieve a domain report.
86-
resp = vtotal.domain_report("027.ru")
87-
88-
# Put a comment onto a specific resource.
89-
resp = vtotal.put_comment(
90-
"9f101483662fc071b7c10f81c64bb34491ca4a877191d464ff46fd94c7247115",
91-
comment="#watchout, this looks very malicious!",
92-
)
93-
94-
pprint(resp)
61+
API_KEY="Insert API key here.",
62+
API_VERSION="v3",
63+
PROXIES={"http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080"},
64+
TIMEOUT=5.0)
65+
```
66+
67+
Additionally, it is possible to provide an API key via the environment variable `VIRUSTOTAL_API_KEY`.
68+
69+
Bash example:
70+
71+
```bash
72+
export VIRUSTOTAL_API_KEY="Insert API key here."
73+
```
74+
75+
PowerShell example:
76+
77+
```powershell
78+
$Env:VIRUSTOTAL_API_KEY = "Insert API key here."
79+
```
80+
81+
Now, initialise the `Virustotal` class:
82+
83+
```python
84+
from virustotal_python import Virustotal
85+
86+
# v2 example
87+
vtotal = Virustotal()
88+
89+
# v3 example
90+
vtotal = Virustotal(API_VERSION="v3")
91+
```
92+
93+
Send a file for analysis:
94+
95+
```python
96+
import os.path
97+
from pprint import pprint
98+
99+
# Declare PATH to file
100+
FILE_PATH = "/path/to/file/to/scan.txt"
101+
102+
# Create dictionary containing the file to send for multipart encoding upload
103+
files = {"file": (os.path.basename(FILE_PATH), open(os.path.abspath(FILE_PATH), "rb"))}
104+
105+
# v2 example
106+
resp = vtotal.request("file/scan", files=files, method="POST")
107+
108+
# The v2 API returns a response_code
109+
# This property retrieves it from the JSON response
110+
print(resp.response_code)
111+
# Print JSON response from the API
112+
pprint(resp.json())
113+
114+
# v3 example
115+
resp = vtotal.request("files", files=files, method="POST")
116+
117+
# The v3 API returns the JSON response inside the 'data' key
118+
# https://developers.virustotal.com/v3.0/reference#api-responses
119+
# This property retrieves the structure inside 'data' from the JSON response
120+
pprint(resp.data)
121+
# Or if you provided COMPATIBILITY_ENABLED=True to the Virustotal class
122+
pprint(resp["json_resp"])
123+
```
124+
125+
Retrieve information about a file:
126+
127+
```python
128+
from pprint import pprint
129+
130+
# The ID (either SHA-256, SHA-1 or MD5) identifying the file
131+
FILE_ID = "9f101483662fc071b7c10f81c64bb34491ca4a877191d464ff46fd94c7247115"
132+
133+
# v2 example
134+
resp = vtotal.request("file/report", {"resource": FILE_ID})
135+
136+
print(resp.response_code)
137+
pprint(resp.json())
138+
139+
# v3 example
140+
resp = vtotal.request(f"files/{FILE_ID}")
141+
142+
pprint(resp.data)
95143
```
96144

145+
Send a URL for analysis, retrieve the analysis report and catch any potential exceptions that may occur (Non 200 HTTP status codes):
146+
97147
```python
98-
# Example resp for url_scan().
99-
# Assuming you have already initiated Virustotal() and imported pprint.
100-
resp = vtotal.url_scan(["ihaveaproblem.info"]) # Query a single url.
101-
pprint(resp)
102-
{'json_resp': {'permalink': 'https://www.virustotal.com/url/fd21590d9df715452c8c000e1b5aa909c7c5ea434c2ddcad3f4ccfe9b0ee224e/analysis/1549973453/',
103-
'resource': 'http://ihaveaproblem.info/',
104-
'response_code': 1,
105-
'scan_date': '2019-02-12 12:10:53',
106-
'scan_id': 'fd21590d9df715452c8c000e1b5aa909c7c5ea434c2ddcad3f4ccfe9b0ee224e-1549973453',
107-
'url': 'http://ihaveaproblem.info/',
108-
'verbose_msg': 'Scan request successfully queued, come back '
109-
'later for the report'},
110-
'status_code': 200}
148+
from virustotal_python import VirustotalError
149+
from pprint import pprint
150+
from base64 import urlsafe_b64encode
151+
152+
url = "ihaveaproblem.info"
153+
154+
# v2 example
155+
try:
156+
# Send a URL to VirusTotal for analysis
157+
resp = vtotal.request("url/scan", params={"url": url}, method="POST")
158+
url_resp = resp.json()
159+
# Obtain scan_id
160+
scan_id = url_resp["scan_id"]
161+
# Request report for URL analysis
162+
analysis_resp = vtotal.request("url/report", params={"resource": scan_id})
163+
print(analysis_resp.response_code)
164+
pprint(analysis_resp.json())
165+
except VirustotalError as err:
166+
print(f"An error occurred: {err}\nCatching and continuing with program.")
167+
168+
# v3 example
169+
try:
170+
# Send URL to VirusTotal for analysis
171+
resp = vtotal.request("urls", data={"url": url}, method="POST")
172+
# URL safe encode URL in base64 format
173+
# https://developers.virustotal.com/v3.0/reference#url
174+
url_id = urlsafe_b64encode(url.encode()).decode().strip("=")
175+
# Obtain the analysis results for the URL using the url_id
176+
analysis_resp = vtotal.request(f"urls/{url_id}")
177+
pprint(analysis_resp.object_type)
178+
pprint(analysis_resp.data)
179+
except VirustotalError as err:
180+
print(f"An error occurred: {err}\nCatching and continuing with program.")
111181
```
112182

113-
## Running Tests
183+
Retrieve information about a domain:
184+
185+
```python
186+
from pprint import pprint
187+
188+
domain = "virustotal.com"
114189

115-
* `Navigate to ./virustotal_python/`
190+
# v2 example
191+
resp = vtotal.request("domain/report", params={"domain": domain})
116192

117-
* `Run the command: pytest -s tests.py`
193+
print(resp.response_code)
194+
pprint(resp.json())
195+
196+
# v3 example
197+
resp = vtotal.request(f"domains/{domain}")
198+
199+
pprint(resp.data)
200+
```
201+
202+
## Running the tests
203+
204+
To run the tests, perform the following steps:
205+
206+
1. Ensure pytest is installed using: `pip install pytest`
207+
208+
2. Export your API key to the environment variable `VIRUSTOTAL_API_KEY` (instructions above).
209+
210+
3. From the root directory of the project run `pytest -s .\virustotal_python\tests.py`
118211

119212
## Changelog
120213

214+
* 0.1.0 - Added support for the VirusTotal v3 API. Library redesign (new usage, examples, tests and more.) See [#24](https://github.com/dbrennand/virustotal-python/pull/24).
215+
121216
* 0.0.9 - Update dependencies for security vulnerability.
122217

123218
* 0.0.8 - Updated dependencies, removed method `file_rescan`
@@ -134,11 +229,11 @@ pprint(resp)
134229

135230
* 0.0.2 - Changes to file_rescan(), file_report(), url_scan(), url_report() to improve ease of use of the wrapper. See issue [#2](https://github.com/dbrennand/virustotal-python/issues/2). Examples updated for changes.
136231

137-
* 0.0.1 - Inital release of virustotal-python. Covered all endpoints of the Virustotal public API.
232+
* 0.0.1 - Initial release of virustotal-python. Covered all endpoints of the Virustotal public API.
138233

139234
## Authors -- Contributors
140235

141-
* **dbrennand** - *Author* - [dbrennand](https://github.com/dbrennand)
236+
* [**dbrennand**](https://github.com/dbrennand) - *Author*
142237

143238
## License
144-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) for details.
239+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) for details.

0 commit comments

Comments
 (0)