Introduction
INLYSE API for all malware classification services. It serves as the central access point for all requests to the backend services. It has several functions that should make the API much more stable. For this purpose a Job and Worker pool was implemented to scale and stabilize the service more easily.
Features
- Fast
- GZIP for responses
- Config file for easy configuration
- Job and Worker pool to scale the service and stabilize it
- Graceful Shutdown and if the server was stopped before all files were analyzed, the analysis will continue after a restart.
- Auto TLS Support without nginx
- PID File
- Rate Limiting
- Hot reload of config file for api keys
- Database
- Basic user statistics and statistic endpoint how many files and how much traffic has already been analyzed
- Implements an inmemory cache to reduce the load on the database and massively increase the access speed.
- Support for ZIP files. Only ZIP Files which contain a single file are supported
- Partial support of encrypted PDF files. We return an error message because we cannot analyze them internally.
Getting started
Dependencies
cURL
import requests
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.12</version>
</dependency>
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.text.MessageFormat;
import(
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"os"
)
To use the provided code, you need to have the following imports/dependencies for the language you are using.
Rate Limits
Request
curl -v --stderr - https://malware.ai/api/stats --header "Authorization: Bearer {api-key}" | grep "x-ratelimit"
key = "{api-key}"
header = {"Authorization": "Bearer {key}".format(key=key)}
response = requests.get("https://www.malware.ai/api/stats", headers=header)
if response.ok:
print("X-Ratelimit-Limit:", response.headers["X-Ratelimit-Limit"])
print("X-Ratelimit-Remaining", response.headers["X-Ratelimit-Remaining"])
print("X-Ratelimit-Reset", response.headers["X-Ratelimit-Reset"])
else:
response.raise_for_status()
String key = "{api-key}";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get_request = new HttpGet("https://www.malware.ai/api/stats");
get_request.addHeader("Authorization", MessageFormat.format("Bearer {0}", key));
HttpResponse response = httpclient.execute(get_request);
System.out.println(Arrays.toString(response.getHeaders("X-Ratelimit-Limit")));
System.out.println(Arrays.toString(response.getHeaders("X-Ratelimit-Remaining")));
System.out.println(Arrays.toString(response.getHeaders("X-Ratelimit-Reset")));
var key = "{api-key}"
request, err := http.NewRequest("GET", "https://www.malware.ai/api/stats", nil)
if err != nil {
log.Fatalln(err)
}
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Fatalln(err)
}
fmt.Println(fmt.Sprintf("X-Ratelimit-Limit: %s", response.Header.Get("X-Ratelimit-Limit")))
fmt.Println(fmt.Sprintf("X-Ratelimit-Remaining: %s", response.Header.Get("X-Ratelimit-Remaining")))
fmt.Println(fmt.Sprintf("X-Ratelimit-Reset: %s", response.Header.Get("X-Ratelimit-Reset")))
Console output (the format can change a little depending on what language you are using)
X-Ratelimit-Limit: int
X-Ratelimit-Remaining: int
X-Ratelimit-Reset: DateTime
The API has rate limits that cannot be exceeded.
x-ratelimit-limit
describes how many requests you can make in total
x-ratelimit-remaining
describes how many requests are remaining
x-ratelimit-reset
describes when the rate limit gets reset
You can check your rate limits like this
If the code does not compile, make sure you installed/imported all the dependencies
Endpoints Overview
/ping
- Test availability
/api/stats/
- Basic user statistics
/api/files/
- Upload and scan a file
/api/files/url
- Scan file at url
/api/analysis/:id
- Retrieve file scan reports for an id
-
/api/analysis?filter=all
- Retrieve recent analyses
Documentation
Ping /ping
Request
curl -s https://www.malware.ai/ping
response = requests.get("https://www.malware.ai/ping")
if response.ok:
print(response.text)
else:
response.raise_for_status()
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get_request = new HttpGet("https://www.malware.ai/ping");
ResponseHandler<String> responseHandler = response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
String responseBody = httpclient.execute(get_request, responseHandler);
System.out.println(responseBody);
resp, err := http.Get("https://www.malware.ai/ping")
if err != nil{
log.Fatalln(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil{
log.Fatalln(err)
}
log.Println(string(body))
Response
PONG
Service still alive?
This is an extra endpoint for the sole purpose of expressing its availability
If the code does not compile, make sure you installed/imported all the dependencies
Stats /api/stats
Request
curl -s https://malware.ai/api/stats --header "Authorization: Bearer {api-key}"
key = "{api-key}"
header = {"Authorization": "Bearer {key}".format(key=key)}
response = requests.get("https://www.malware.ai/api/stats", headers=header)
if response.ok:
print(response.text)
else:
response.raise_for_status()
String key = "{api-key}";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get_request = new HttpGet("https://www.malware.ai/api/stats");
get_request.addHeader("Authorization", MessageFormat.format("Bearer {0}", key));
ResponseHandler<String> responseHandler =
response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
String responseBody = httpclient.execute(get_request, responseHandler);
System.out.println(responseBody);
var key = "{api-key}"
request, err := http.NewRequest("GET", "https://www.malware.ai/api/stats", nil)
if err != nil {
log.Fatalln(err)
}
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Fatalln(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(responseData))
Response
{
"AnalysedFiles": "int",
"Traffic": "int"
}
User statistics
Returns user statistics. How many files and how much traffic has already been analyzed
If the code does not compile, make sure you installed/imported all the dependencies
Scan File /api/files/
Request
curl -s -F 'file=@{absolute_path_to_file}' https://malware.ai/api/files/ --header "Authorization: Bearer {api-key}"
key = "{api-key}"
path = "{absolute_path_to_file}"
header = {"Authorization": "Bearer {key}".format(key=key)}
files = {"file": open("{path}".format(path=path), "br",)}
response = requests.post(
"https://www.malware.ai/api/files/", files=files, headers=header
)
if response.ok:
print(response.text)
else:
response.raise_for_status()
String path = "{absolute_path_to_file}";
String key = "{api-key}";
File file = new File(path);
MultipartEntityBuilder builder =
MultipartEntityBuilder.create()
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.addBinaryBody("file", file, ContentType.DEFAULT_BINARY, "filename");
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost post_request = new HttpPost("https://www.malware.ai/api/files/");
post_request.addHeader("Authorization", MessageFormat.format("Bearer {0}", key));
ResponseHandler<String> responseHandler =
response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
post_request.setEntity(builder.build());
String responseBody = httpclient.execute(post_request, responseHandler);
System.out.println(responseBody);
var path = "{absolute_path_to_file}"
var key = "{api-key}"
file, err := os.Open(path)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
var requestBody bytes.Buffer
multiPartWriter := multipart.NewWriter(&requestBody)
fileWriter, err := multiPartWriter.CreateFormFile("file", "filename")
if err != nil {
log.Fatalln(err)
}
_, err = io.Copy(fileWriter, file)
multiPartWriter.Close()
request, err := http.NewRequest("POST", "https://www.malware.ai/api/files/", &requestBody)
if err != nil {
log.Fatalln(err)
}
request.Header.Set("Content-Type", multiPartWriter.FormDataContentType())
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Fatalln(err)
}
responseData,err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(responseData))
Response
{
"id":"492366f7-1806-4d33-a9bb-eac4ef2007ce"
}
Upload and scan a file
This endpoint allows you to upload a file for scanning. Before performing your submissions we encourage you to retrieve the latest report on the file, if it is recent enough you might want to save time and bandwidth by making use of it.
Restrictions
File size limit is 17MB File format has to be PDF or a ZIP with one PDF inside.
If the code does not compile, make sure you installed/imported all the dependencies
Scan File by URL /api/files/url
Request
curl -s -X POST https://malware.ai/api/files/url --header "Authorization: Bearer {api-key}" --header 'Content-Type: application/json' -d '{"url":"{url}"}'
key = "{api-key}"
file_url = "{url}"
headers = {"Authorization": "Bearer {key}".format(key=key)}
data = {"url": file_url}
response = requests.post(
"https://www.malware.ai/api/files/url", headers=headers, data=data
)
if response.ok:
print(response.text)
else:
response.raise_for_status()
String file_url = "{url}";
String key = "{api-key}";
List<NameValuePair> data = new ArrayList<>(0);
data.add(new BasicNameValuePair("url", file_url));
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost post_request = new HttpPost("https://www.malware.ai/api/files/url");
post_request.addHeader("Authorization", MessageFormat.format("Bearer {0}", key));
ResponseHandler<String> responseHandler =
response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
post_request.setEntity(new UrlEncodedFormEntity(data, "UTF-8"));
String responseBody = httpclient.execute(post_request, responseHandler);
System.out.println(responseBody);
var data = []byte(`{"url":"{url}"}`)
var key = "{api-key}"
request, err := http.NewRequest("POST", "https://www.malware.ai/api/files/url", bytes.NewBuffer(data))
if err != nil {
log.Fatalln(err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Fatalln(err)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(responseData))
Response
{
"id":"492366f7-1806-4d33-a9bb-eac4ef2007ce"
}
Scan a file by URL
This endpoint allows you to submit a file for scanning, by uploading an url pointing to the file.
Restrictions
File size limit is 17MB File format has to be PDF or a ZIP with one PDF inside.
If the code does not compile, make sure you installed/imported all the dependencies
Retrieve Classification /api/analysis/:id
Request
curl -s https://malware.ai/api/analysis/{analysis_id} --header "Authorization: Bearer {api-key}"
a_id = "{analysis-id}"
key = "{api-key}"
header = {"Authorization": "Bearer {key}".format(key=key)}
response = requests.get(
"https://www.malware.ai/api/analysis/{id}".format(id=a_id),
headers=header,
)
if response.ok:
print(response.text)
else:
response.raise_for_status()
String id = "{analysis-id}";
String key = "{api-key}";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get_request =
new HttpGet(
MessageFormat.format("https://www.malware.ai/api/analysis/{0}", id));
get_request.addHeader("Authorization", MessageFormat.format("Bearer {0}", key));
ResponseHandler<String> responseHandler =
response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
String responseBody = httpclient.execute(get_request, responseHandler);
System.out.println(responseBody);
var id = "{analysis-id}"
var key = "{api-key}"
request, err := http.NewRequest("GET", fmt.Sprintf("https://www.malware.ai/api/analysis/%s", id), nil)
if err != nil {
log.Fatalln(err)
}
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Fatalln(err)
}
responseData,err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(responseData))
Response normal
{
"ID":"0b7d2cde-1d5f-4a59-961e-928569af46dd",
"MD5":"d9583012ec400655dfda421c3ce6b225",
"SHA1":"13e1b5be530c46f040e668d52d48e00ed2b9e986",
"SHA256":"4bff806b8f05235cfca3e80ec19d271ebec33a1a8ece84c12a7ffd3a6348c3a7",
"SHA512":"71c92e95a209a2aecb5f01f4a5ac2f396ab31199b2c932c688141db53dca122df1d9d20ae3c7951a6fea7c6a301fe9375105b0b5852f99ec5d2a71fc8a917861",
"Filename":"file.pdf",
"Size":174811,
"FileType":"application/pdf",
"Label":"benign"
}
Response extended
{
"ID":"9d3db18f-c67a-428f-816b-8bf15a4344c6",
"MD5":"5b8865422e95462ecb6cb23ba8597cc9",
"SHA1":"5a105b34b1f1eabbc4b261477b60fa3b30408f54",
"SHA256":"fc0dd80c491c2cd88f3bb863affa313f118c009640ecb8573a192117628e9b28",
"SHA512":"f3827e583a3d882b5ad39b83dae879d1733836eb7dbb8fefdbfac932acd42fdd3655a0f216ef51c63450f65dd89955be521b32cc90e1b9fa0449e1f0770a622b",
"Filename":"test.pdf",
"Size":12344,
"FileType":"application/pdf",
"Label":"malicious",
"ScoreBenign":"0.3390300050287237",
"ScoreMalicious":"0.6609699949712763"
}
Retrieve file scan reports
Returns the data of the analysis depending on the API key. There are three different classes. Normal and Extended Keys. Extenden keys also return the benign and malicous Scores in addition to the normal information.
If the code does not compile, make sure you installed/imported all the dependencies
List Analysis /api/analysis?filter=all
Request
curl -s https://malware.ai/api/analysis?filter={search-filter} --header "Authorization: Bearer {api-key}"
key = "{api-key}"
s_filter = {"search-filter"}
header = {"Authorization": "Bearer {key}".format(key=key)}
response = requests.get(
"https://www.malware.ai/api/analysis?filter={filter}".format(filter=s_filter),
headers=header,
)
if response.ok:
print(response.text)
else:
response.raise_for_status()
String filter = "{search-filter}";
String key = "{api-key}";
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get_request =
new HttpGet(MessageFormat.format("https://www.malware.ai/api/analysis?filter={0}", filter));
get_request.addHeader("Authorization", MessageFormat.format("Bearer {0}", key));
ResponseHandler<String> responseHandler =
response -> {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
String responseBody = httpclient.execute(get_request, responseHandler);
System.out.println(responseBody);
var filter = "{search-filter}"
var key = "{api-key}"
request, err := http.NewRequest("GET", fmt.Sprintf("https://www.malware.ai/api/analysis?filter=%s", filter), nil)
if err != nil {
log.Fatalln(err)
}
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", key))
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
log.Fatalln(err)
}
responseData,err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(responseData))
Response
["1c67bcf5-6458-43bb-a7a9-f8a0d4e86d30"]
Retrieve a list of submitted files
Returns a list with the last 1000 analyses. It is possible to specify a filter.
Possible Filters are: all, finished, unfinished, error
If the code does not compile, make sure you installed/imported all the dependencies
Errors
The API returns normal HTTP status codes to indicate if the request was successful or not.
Status Code | Explanation |
---|---|
200 - OK | successful request |
201 - Created | The analysis just started |
202 - Accepted | The analysis is still in progress |
400 - Bad Request | There was an error in the request (perhaps a missing parameter) |
401 - Unauthorized | The provided API-key is not valid (or missing) |
404 - Not Found | The requested API endpoint does not exist |
500 - Internal Server Error | Something went wrong and you should try to upload the file again |