forked from detectify/api-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdetectify.cs
185 lines (164 loc) · 7.43 KB
/
detectify.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace DetectifyExample
{
internal class Program
{
public static async Task Main(string[] args)
{
// Detectify keys
var apiKey = "d4bf676ee6146557cbf0f28fe6cbc290";
var secretKey = "SGVsbG8sIHdvcmxkISBJIGFtIGEgdGVhcG90IQ==";
// Token for the scan profile
var scanProfile = "5605b488634efe810dff4276e28ca7f9";
// Create the API client
var detectify = new Detectify(apiKey, secretKey);
// Start a scan
bool started = await detectify.StartScanAsync(scanProfile);
if (started)
{
// Show scan status if scan successfully started
await detectify.ScanStatusAsync(scanProfile);
}
}
}
public class Detectify
{
/// <summary>
/// Detectify API endpoint, without a trailing slash.
/// </summary>
private const string Endpoint = "https://api.detectify.com/rest/v2";
private string ApiKey { get; }
private string SecretKey { get; }
public Detectify(string apiKey, string secretKey)
{
ApiKey = apiKey;
SecretKey = secretKey;
}
/// <summary>
/// Create the headers used to sign an API request.
/// </summary>
/// <param name="method">The method used for the call, in uppercase.</param>
/// <param name="path">The path of the request, ie `/v2/domains/`.</param>
/// <param name="timestamp">The timestamp used when creating the signature.</param>
/// <param name="body">The body used for requests that require a provided payload. Must be null or an empty string if the request has no body.</param>
/// <returns>Returns a dictionary of signature headers to use with an API call.</returns>
private Dictionary<string, string> MakeHeaders(string method, string path,
DateTime timestamp, string body)
{
// Signature timestamp is in Unix epoch format
var epoch = (long) (timestamp - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds;
// Calculate signature hash
var signatureValue = $"{method};{path};{ApiKey};{epoch};{body}";
var signatureBytes = new HMACSHA256(Convert.FromBase64String(SecretKey))
.ComputeHash(Encoding.Default.GetBytes(signatureValue));
var signature = Convert.ToBase64String(signatureBytes);
return new Dictionary<string, string>
{
["X-Detectify-Key"] = ApiKey,
["X-Detectify-Signature"] = signature,
["X-Detectify-Timestamp"] = epoch.ToString()
};
}
/// <summary>
/// Start a scan for a given scan profile.
/// </summary>
/// <param name="scanProfile">The scan profile to start a scan on.</param>
/// <returns>Returns true if a scan was started, false if not.</returns>
public async Task<bool> StartScanAsync(string scanProfile)
{
var path = $"/scans/{scanProfile}/";
var url = $"{Endpoint}{path}";
var timestamp = DateTime.UtcNow;
// Create Detectify headers
var headers = MakeHeaders("POST", path, timestamp, null);
using (var client = new HttpClient())
{
// Add Detectify headers to request
headers.ToList().ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));
var response = await client.PostAsync(url, null);
switch ((int) response.StatusCode)
{
case 202:
Console.WriteLine("Scan start request accepted");
return true;
case 400:
Console.WriteLine("Invalid scan profile token");
return false;
case 401:
Console.WriteLine("Missing/invalid API key or message signature, or invalid timestamp");
return false;
case 403:
Console.WriteLine("The API key cannot access this functionality");
return false;
case 404:
Console.WriteLine(
"The specified scan profile does not exist or the API cannot access the profile");
return false;
case 409:
Console.WriteLine("A scan is already running on the specified profile");
return false;
case 423:
Console.WriteLine("The domain is not verified");
return false;
case 500:
case 503:
Console.WriteLine("An error occurred while processing the request");
return false;
default:
Console.WriteLine($"API returned unhandled status code: {(int) response.StatusCode}");
return false;
}
}
}
/// <summary>
/// Retrieves the status of a currently running scan for a given scan profile.
/// </summary>
/// <param name="scanProfile">The scan profile token to check scan status for.</param>
public async Task ScanStatusAsync(string scanProfile)
{
var path = $"/scans/{scanProfile}/";
var url = $"{Endpoint}{path}";
var timestamp = DateTime.UtcNow;
// Create Detectify headers
var headers = MakeHeaders("GET", path, timestamp, null);
using (var client = new HttpClient())
{
// Add Detectify headers to request
headers.ToList().ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));
var response = await client.GetAsync(url);
switch ((int) response.StatusCode)
{
case 200:
Console.WriteLine(await response.Content.ReadAsStringAsync());
break;
case 400:
Console.WriteLine("Invalid scan profile token");
break;
case 401:
Console.WriteLine("Missing/invalid API key or message signature, or invalid timestamp");
break;
case 403:
Console.WriteLine("The API key cannot access this functionality");
break;
case 404:
Console.WriteLine(
"No scan running for the specified profile, or the specified scan profile does not exist, or the API cannot access the profile");
break;
case 500:
case 503:
Console.WriteLine("An error occurred while processing the request");
break;
default:
Console.WriteLine($"API returned unhandled status code: {(int) response.StatusCode}");
break;
}
}
}
}
}