Skip to content

Commit 1125bdf

Browse files
committed
Introduces support for .NET 8. Code refactoring and general library maintenance.
1 parent 2ad4c3b commit 1125bdf

15 files changed

Lines changed: 337 additions & 265 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Build and Release
2+
3+
env:
4+
DOTNET_VERSION: '8.x'
5+
NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json'
6+
BUILD_DIRECTORY: '${{ github.workspace }}/build'
7+
8+
on:
9+
push:
10+
tags:
11+
- 'v*.*.*'
12+
13+
jobs:
14+
build-and-release:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout Repository
19+
uses: actions/checkout@v2
20+
21+
- name: Get Version
22+
id: get_version
23+
run: |
24+
echo "tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
25+
echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
26+
27+
- name: Get Project Metadata
28+
id: get_project_meta
29+
run: |
30+
name=$(echo '${{ github.repository }}' | cut -d '/' -f 2)
31+
32+
echo "name=${name}" >> $GITHUB_OUTPUT
33+
echo "path=${name}/${name}.csproj" >> $GITHUB_OUTPUT
34+
35+
- name: Setup .NET
36+
uses: actions/setup-dotnet@v3.2.0
37+
with:
38+
dotnet-version: ${{ env.DOTNET_VERSION }}
39+
40+
- name: Restore Packages
41+
run: dotnet restore ${{ steps.get_project_meta.outputs.path }}
42+
43+
- name: Build Project
44+
run: dotnet build ${{ steps.get_project_meta.outputs.path }} /p:ContinuousIntegrationBuild=true --no-restore --configuration Release
45+
46+
- name: Pack Project
47+
run: dotnet pack ${{ steps.get_project_meta.outputs.path }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.get_version.outputs.version }} --output ${{ env.BUILD_DIRECTORY }}
48+
49+
- name: Push Package
50+
env:
51+
NUGET_AUTH_TOKEN: ${{ secrets.NUGET_AUTH_TOKEN }}
52+
run: dotnet nuget push ${{ env.BUILD_DIRECTORY }}/*.nupkg -k $NUGET_AUTH_TOKEN -s ${{ env.NUGET_SOURCE_URL }}
53+
54+
- name: Create Release
55+
uses: softprops/action-gh-release@v1
56+
with:
57+
token: ${{ secrets.GITHUB_TOKEN }}
58+
name: ${{ steps.get_version.outputs.tag }}
59+
body: ${{ github.event.head_commit.message }}
60+
files: '${{ env.BUILD_DIRECTORY }}/*'

Example/Example.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
5+
<TargetFrameworks>net8.0</TargetFrameworks>
66
</PropertyGroup>
77

88
<ItemGroup>

Example/Program.cs

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ namespace Example
99
{
1010
public static class Program
1111
{
12+
private static UrlscanClient Client;
13+
private static LiveClient Live;
14+
1215
public static async Task Main()
1316
{
14-
Console.WriteLine("Enter your Urlscan API Key:");
17+
Console.WriteLine("> Enter your Urlscan API Key:");
1518
string key = Console.ReadLine();
1619

17-
Console.WriteLine("Enter your Urlscan SID, or press enter to skip:");
20+
Console.WriteLine("\n> Enter your Urlscan SID, or press enter to skip:");
1821
string sid = Console.ReadLine();
1922
if (string.IsNullOrEmpty(sid)) sid = null;
2023

21-
UrlscanClient client = new(key, sid);
24+
Console.WriteLine();
25+
Client = new(key, sid);
2226

23-
User user = await client.GetCurrentUser();
27+
User user = await Client.GetCurrentUser();
2428

2529
Console.WriteLine($"Current user info:");
2630
Console.WriteLine($"Registered: {(int)(DateTime.Now - user.CreatedAt).TotalDays} days ago");
@@ -41,12 +45,12 @@ public static async Task Main()
4145
Console.WriteLine($"You've made {user.Stats.Total} sumissions, of which {user.Stats.Private + user.Stats.Unlisted} were private");
4246
Console.WriteLine();
4347

44-
Stats stats = await client.GetStats();
48+
Stats stats = await Client.GetStats();
4549
Console.WriteLine($"Currently running scan tasks: {stats.Running}");
4650
Console.WriteLine($"24h stats: public: {stats.Public}, unlisted: {stats.Unlisted}, private: {stats.Private}");
4751
Console.WriteLine($"Total: {stats.Total}\n");
4852

49-
Console.Write($"Enter a URL to scan: ");
53+
Console.Write($"\n> Enter a URL to scan: ");
5054

5155
string url = Console.ReadLine();
5256

@@ -55,41 +59,36 @@ public static async Task Main()
5559
Submission subm;
5660
try
5761
{
58-
subm = await client.Scan(new ScanParameters()
62+
subm = await Client.Scan(new ScanParameters()
5963
{
60-
//URL is the only necessary argument, the rest is all optional.
64+
//URL is the only mandatory argument, the rest is all optional.
6165
Url = url,
62-
Tags = new string[] { "test" },
66+
Tags = ["test"],
6367
Country = ScanCountry.FI,
6468
UserAgent = "My-Custom-Scanner/1.0.0",
6569
OverrideSafety = false,
6670
Referer = "https://google.com",
6771
Visibility = Visibility.Public
6872
});
6973
}
70-
catch (NxDomainException ex)
74+
catch (UrlscanException ex)
7175
{
72-
Console.WriteLine($"The domain doesn't have any DNS records.\nUrlscan Message: {ex.Message}");
73-
Console.ReadKey();
74-
return;
75-
}
76-
catch (SillyException ex)
77-
{
78-
Console.WriteLine($"You've entered a malformed hostname/URL.\nUrlscan Message: {ex.Message}");
76+
Console.WriteLine($"Received an Urlscan exception: {ex.Message}");
77+
if (ex.Error is not null) Console.WriteLine($"{ex.Error.Description} => {ex.Error.Message}");
7978
Console.ReadKey();
8079
return;
8180
}
8281
catch (Exception ex)
8382
{
84-
Console.WriteLine($"Received an unknown exception while scanning of type {ex.GetType().Name}\nMessage: {ex.Message}");
83+
Console.WriteLine($"Received an unknown exception while scanning: {ex.GetType().Name} => {ex.Message}");
8584
Console.ReadKey();
8685
return;
8786
}
8887

8988
Console.WriteLine($"Submission created: {subm.UUID}\n");
9089

9190
Console.WriteLine($"Waiting for the scan to finish, this will take about 10 seconds.\n");
92-
Result res = await client.Poll(subm);
91+
Result res = await Client.Poll(subm);
9392

9493
Console.WriteLine($"Page was successfully scanned by a submitter from {res.Submitter.Country}.\n");
9594

@@ -129,63 +128,63 @@ public static async Task Main()
129128
Console.WriteLine($"Malicious: {cver.Malicious}\n");
130129
}
131130

132-
SimilarScan[] similar = await client.GetSimilarScans("bc1ef5f2-eddc-40ae-86c9-fb5894b5d1f2");
131+
SimilarScan[] similar = await Client.GetSimilarScans("bc1ef5f2-eddc-40ae-86c9-fb5894b5d1f2");
133132
Console.WriteLine($"Found {similar.Length} similar scans");
134133

135134
Console.WriteLine($"Done analysing URL\n");
136135

137-
Console.WriteLine($"Downloading screenshot (screenshot.png) and DOM (dom.html) to the current directory.");
138-
File.WriteAllBytes("screenshot.png", await client.DownloadScreenshot(res));
139-
File.WriteAllText("dom.html", await client.DownloadDOM(res));
140-
Console.WriteLine();
141-
Console.WriteLine();
136+
Console.WriteLine($"\n> Downloading screenshot (screenshot.png) and DOM (dom.html) to the current directory.");
137+
File.WriteAllBytes("screenshot.png", await Client.DownloadScreenshot(res));
138+
File.WriteAllText("dom.html", await Client.DownloadDOM(res));
142139

143-
if (client.UsesAccountSID)
140+
if (Client.UsesAccountSID)
144141
{
145-
Console.WriteLine("Press any key to submit a verdict to a known Discord phishing site.");
142+
Console.WriteLine("\n> Press any key to submit a verdict to a known Discord phishing site.");
146143
Console.ReadKey();
147144

148-
await client.AddVerdict(new VerdictParameters()
145+
await Client.AddVerdict(new VerdictParameters()
149146
{
150147
UUID = "8964cc71-ea31-476c-ba8f-863bf4bf6b2f",
151148
Comment = "Running a Discord phishing scam with Discord HypeSquad as their target.",
152149
Scope = VerdictScope.PageDomain,
153150
ScopeValue = "contact-hype-testers.com",
154-
ThreatTypes = new ThreatType[]
155-
{
151+
ThreatTypes =
152+
[
156153
ThreatType.Phishing,
157154
ThreatType.BrandImpersonation
158-
},
159-
Brands = new string[]
160-
{
155+
],
156+
Brands =
157+
[
161158
"Discord"
162-
},
159+
],
163160
Verdict = VerdictType.Malicious
164161
});
165162

166-
Console.WriteLine("Successfully verdicted, see it at: https://urlscan.io/result/8964cc71-ea31-476c-ba8f-863bf4bf6b2f/#verdicts\n");
163+
Console.WriteLine("Successfully verdicted, see it at: https://urlscan.io/result/8964cc71-ea31-476c-ba8f-863bf4bf6b2f/#verdicts");
167164

168-
Console.WriteLine("Press any key to search for scans that contain hypesquad in them.");
165+
Console.WriteLine("\n> Press any key to search for scans that contain hypesquad in them.");
169166
Console.ReadKey();
170167

171-
SearchItem[] scans = await client.Search("page.status:200 AND domain.keyword:*hypesquad*", 10);
168+
SearchItem[] scans = await Client.Search("page.status:200 AND domain.keyword:*hypesquad*", 10);
172169
foreach (SearchItem scan in scans)
173170
{
174171
Console.WriteLine(scan.Page.Url[..Math.Min(scan.Page.Url.Length, 50)]);
175172
}
176173
}
177174

178-
Console.WriteLine("Press any key to start watching for newly scanned URLs.");
175+
Console.WriteLine("\n> Press any key to start watching for newly scanned URLs.");
179176
Console.ReadKey();
180177

181-
LiveClient live = new(1000 * 3, 10);
182-
live.UrlScanned += (sender, scan) =>
178+
Live = new(1000 * 3, 10);
179+
180+
Live.UrlScanned += (sender, scan) =>
183181
{
184182
Console.WriteLine(scan.Task.Url[..Math.Min(scan.Task.Url.Length, 50)]);
185183
};
186184

187-
Console.WriteLine("Demo finished, press any key to exit.");
185+
Console.WriteLine("\n> Demo finished, press any key to exit.");
188186
Console.ReadKey();
187+
Live.Stop();
189188
}
190189
}
191190
}

README.md

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
</div>
1010

1111
## Usage
12-
Provides an easy interface for interacting with the Urlscan API.
13-
12+
This library provides an easy interface for interacting with the Urlscan API.
1413
You can use this library to automate your Urlscan submissions, search for existing scans, track newly submitted scans and analyse network activity of malicious websites.
1514

16-
To get started, add the library into your solution with either the `NuGet Package Manager` or the `dotnet` CLI.
15+
To get started, import the library into your solution with either the `NuGet Package Manager` or the `dotnet` CLI.
1716
```rust
1817
dotnet add package Urlscan
1918
```
@@ -26,20 +25,23 @@ using Urlscan;
2625
Need more examples? Under the `Example` directory you can find a working demo project that implements this library.
2726

2827
### Obtaining API keys
29-
> API keys can be created in the user section `Settings & API`
28+
API keys can be created under the user section `Settings & API`.
3029

31-
> Secure identifier SID cookies can be obtained from your browser's cookie storage. Make sure to only copy the value, without the name!
30+
Secure identifier SID cookies can be obtained from your browser's cookie storage. Make sure to only copy the value, without the name!
3231

33-
## Features
34-
- Built for **.NET 6** and **.NET 7**
32+
## Properties
33+
- Built for **.NET 8**, **.NET 7** and **.NET 6**
3534
- Fully **async**
3635
- Extensive **XML documentation**
37-
- Coverage of the free API endpoints, including some user-only and beta routes
38-
- **No external dependencies** (uses integrated HTTP and JSON)
39-
- **Custom exceptions** (`UrlscanException`) for advanced catching
40-
- Automatic **ratelimit** handling and request **retries**
36+
- Coverage of the free API endpoints, including some user-only and beta routes
37+
- **No external dependencies** (makes use of built-in `HttpClient` and `JsonSerializer`)
38+
- **Custom exceptions** (`UrlscanException`) for easy debugging
39+
- Parsing of custom Urlscan errors
4140
- Example project to demonstrate all capabilities of the library
41+
42+
## Features
4243
- Scan suspicious URLs and submit verdicts on them
44+
- Search for public scans using ElasticSearch queries
4345
- Download screenshots and page DOMs
4446
- Empower your threat intelligence with live scans through `LiveClient`
4547

@@ -109,30 +111,8 @@ await client.AddVerdict(new VerdictParameters()
109111
});
110112
```
111113

112-
## Available Methods
113-
- Task **AddVerdict**(Result result, VerdictScope scope, VerdictType type, string comment, string[] brands, ThreatType[] threats)
114-
- Task **AddVerdict**(string uuid, VerdictScope scope, string scopeValue, VerdictType type, string comment, string[] brands, ThreatType[] threats)
115-
- Task **AddVerdict**(VerdictParameters parameters)
116-
- Task\<byte[]> **DownloadScreenshot**(Result result)
117-
- Task\<byte[]> **DownloadScreenshot**(string uuid)
118-
- Task\<byte[]> **Liveshot**(string url, int width = 1280, int height = 1024)
119-
- Task\<Result> **GetResult**(string uuid)
120-
- Task\<Result> **Poll**(string uuid, int delay = 5000, int interval = 2000)
121-
- Task\<Result> **Poll**(Submission submission, int delay = 5000, int interval = 2000)
122-
- Task\<SearchItem[]> **Search**(string query, int amount = 100, string targetScan = null)
123-
- Task\<SimilarScan[]> **GetSimilarScans**(string uuid)
124-
- Task\<Stats> **GetStats**()
125-
- Task\<Stream> **DownloadScreenshotStream**(string uuid)
126-
- Task\<Stream> **LiveshotStream**(string url, int width = 1280, int height = 1024)
127-
- Task\<string> **DownloadDOM**(Result result)
128-
- Task\<string> **DownloadDOM**(string uuid)
129-
- Task\<Submission> **Scan**(string url, string[] tags = null, string userAgent = null, string referer = null, bool overrideSafety = false, Visibility visibility = Visibility.Public, ScanCountry country = ScanCountry.Auto)
130-
- Task\<Submission> **Scan**(ScanParameters parameters)
131-
- Task\<User> **GetCurrentUser**()
132-
133-
## Available Events
134-
- EventHandler\<LiveScan> `UrlScanned`
135-
136114
## References
137-
- https://urlscan.io
138-
- https://twitter.com/urlscanio
115+
- Official website: https://urlscan.io
116+
- Urlscan Twitter: https://twitter.com/urlscanio
117+
118+
*This is a community-ran library. Not affiliated with Urlscan GmbH.*

0 commit comments

Comments
 (0)