Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support urlscan.io submit/scan visibility #823

Merged
merged 1 commit into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/command/runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { err, errAsync, ok, okAsync, Result, ResultAsync } from "neverthrow";

import { URLScan } from "~/scanner";
import type { OptionsType } from "~/schemas";
import { Selector } from "~/selector";
import type {
Expand Down Expand Up @@ -139,6 +140,7 @@ export class CommandRunner {
break;
case "urlscan.io":
scanner.setAPIKey(this.options.urlscanAPIKey);
(scanner as URLScan).setVisibility(this.options.urlscanVisibility);
break;
case "VirusTotal":
scanner.setAPIKey(this.options.virusTotalAPIKey);
Expand Down
20 changes: 19 additions & 1 deletion src/options/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "bulma/css/bulma.css";
import { onMounted, reactive, ref, watch } from "vue";

import { Scanners } from "../scanner";
import type { OptionsType, SearchableType } from "../schemas";
import { OptionsType, SearchableType } from "../schemas";
import { Searchers } from "../searcher";
import { getOptions, setOptions } from "../storage";
import type { ScannableType, Scanner, Searcher } from "../types";
Expand All @@ -31,6 +31,7 @@ const options = reactive<OptionsType>({
strict: true,
hybridAnalysisAPIKey: undefined,
urlscanAPIKey: undefined,
urlscanVisibility: "public",
virusTotalAPIKey: undefined,
disabledScannerNames: [],
disabledSearcherNames: [],
Expand Down Expand Up @@ -267,6 +268,23 @@ watch(options, async (newOptions) => {
/>
</div>
</div>
<div
class="field"
v-if="
isSelectedScanner(scanner) && scanner.name === 'urlscan.io'
"
>
<label class="label">Visibility</label>
<div class="control">
<div class="select">
<select v-model="options.urlscanVisibility">
<option>public</option>
<option>unlisted</option>
<option>private</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="box" id="searchers">
Expand Down
27 changes: 15 additions & 12 deletions src/scanner/urlscan.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { errAsync, ResultAsync } from "neverthrow";
import * as v from "valibot";

import type { urlscanVisibilityType } from "~/schemas";
import type { ScannableType } from "~/types";

import { Base } from "./base";
Expand All @@ -19,6 +20,7 @@ export class URLScan extends Base {
public name: string;
public supportedTypes: ScannableType[] = ["ip", "domain", "url"];
public apiKey?: string = undefined;
public visibility: urlscanVisibilityType = "public";

public constructor() {
super();
Expand All @@ -30,6 +32,10 @@ export class URLScan extends Base {
this.apiKey = apiKey;
}

public setVisibility(visibility: urlscanVisibilityType): void {
this.visibility = visibility;
}

scanByIP(ip: string) {
return this.scan(ip);
}
Expand All @@ -42,25 +48,22 @@ export class URLScan extends Base {
return this.scan(url);
}

private scan(query: string, isPublic = true) {
private scan(query: string) {
if (!this.apiKey) {
return errAsync("Please set your urlscan.io API key via the option.");
}

const body = JSON.stringify({
public: isPublic ? "on" : "off",
url: query,
});
const headers = {
"API-KEY": this.apiKey,
"content-type": "application/json",
};

const scan = async () => {
const res = await fetch(`${this.baseURL}/api/v1/scan/`, {
method: "POST",
headers,
body,
headers: {
"API-KEY": this.apiKey!,
"content-type": "application/json",
},
body: JSON.stringify({
visibility: this.visibility,
url: query,
}),
});

const data = await res.json();
Expand Down
9 changes: 9 additions & 0 deletions src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ export const SelectorOptionsSchema = v.object({

export type SelectorOptionsType = v.InferOutput<typeof SelectorOptionsSchema>;

export const urlscanVisibility = v.union([
v.literal("public"),
v.literal("unlisted"),
v.literal("private"),
]);

export type urlscanVisibilityType = v.InferOutput<typeof urlscanVisibility>;

export const OtherOptionsSchema = v.object({
href: v.optional(v.boolean(), true),
disabledSearcherNames: v.optional(v.array(v.string()), []),
disabledScannerNames: v.optional(v.array(v.string()), []),
hybridAnalysisAPIKey: v.optional(v.string()),
urlscanAPIKey: v.optional(v.string()),
urlscanVisibility: v.optional(urlscanVisibility, "public"),
virusTotalAPIKey: v.optional(v.string()),
});

Expand Down
1 change: 1 addition & 0 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export async function setOptions(options: OptionsType): Promise<void> {
disabledScannerNames: options.disabledScannerNames.map((n) => n),
hybridAnalysisAPIKey: options.hybridAnalysisAPIKey,
urlscanAPIKey: options.urlscanAPIKey,
urlscanVisibility: options.urlscanVisibility,
virusTotalAPIKey: options.virusTotalAPIKey,
},
});
Expand Down