Skip to content

Commit 75e4d85

Browse files
jaspermayonegithub-advanced-security[bot]claude[bot]
authored
fix: add graceful error handling for VirusTotal invalid domains (#111)
Co-authored-by: Jasper Mayone <jaspermayone@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
1 parent cf6ebb8 commit 75e4d85

11 files changed

Lines changed: 52 additions & 18 deletions

File tree

src/func/domain/parseData.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,12 @@ export async function parseData(
4747
} else if (urlScanData.verdicts.overall.malicious === true) {
4848
verdict = true;
4949
} else if (
50-
virusTotalData.data.attributes.last_analysis_stats.malicious > 0 ||
51-
virusTotalData.data.attributes.last_analysis_stats.suspicious > 0
50+
virusTotalData &&
51+
virusTotalData.data &&
52+
virusTotalData.data.attributes &&
53+
virusTotalData.data.attributes.last_analysis_stats &&
54+
(virusTotalData.data.attributes.last_analysis_stats.malicious > 0 ||
55+
virusTotalData.data.attributes.last_analysis_stats.suspicious > 0)
5256
) {
5357
verdict = true;
5458
// todo: add correct condition for abuseChData

src/services/AbuseCh.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class AbuseChService {
2626
}
2727
);
2828

29-
let sanitizedDomain = await sanitizeDomain(domain);
29+
let sanitizedDomain = sanitizeDomain(domain);
3030
const dbDomain = await getDbDomain(sanitizedDomain);
3131

3232
// Check if the response is JSON before saving to database

src/services/GoogleSafebrowsing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class GoogleSafebrowsingService {
1919
check: async (domain: string) => {
2020
// metrics.increment("services.googleSafebrowsing.domain.check");
2121

22-
const sanitizedDomain = await sanitizeDomain(domain);
22+
const sanitizedDomain = sanitizeDomain(domain);
2323

2424
const response = await axios.post(
2525
`https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${process

src/services/IpQualityScore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class IpQualityScoreService {
2020
check: async (domain: string) => {
2121
// metrics.increment("services.ipqualityscore.domain.check");
2222

23-
const sanitizedDomain = await sanitizeDomain(domain);
23+
const sanitizedDomain = sanitizeDomain(domain);
2424

2525
const response = await axios.get(
2626
`https://ipqualityscore.com/api/json/url/${process.env

src/services/PhishReport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class PhishReportService {
1919
check: async (domain: string) => {
2020
// metrics.increment("services.phishreport.domain.check");
2121

22-
const sanitizedDomain = await sanitizeDomain(domain);
22+
const sanitizedDomain = sanitizeDomain(domain);
2323

2424
let response = await axios.get(
2525
`https://phish.report/api/v0/hosting?url=${sanitizedDomain}`,

src/services/SecurityTrails.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class SecurityTrailsService {
1919
check: async (domain: string) => {
2020
// metrics.increment("services.securitytrails.domain.check");
2121

22-
const sanitizedDomain = await sanitizeDomain(domain);
22+
const sanitizedDomain = sanitizeDomain(domain);
2323

2424
const options = {
2525
method: "GET",

src/services/SinkingYahts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class SinkingYahtsService {
1818
*/
1919
check: async (domain: string) => {
2020
// metrics.increment("services.sinkingyahts.domain.check");
21-
const sanitizedDomain = await sanitizeDomain(domain);
21+
const sanitizedDomain = sanitizeDomain(domain);
2222

2323
const response = await axios.get<boolean>(
2424
`https://phish.sinking.yachts/v2/check/${sanitizedDomain}`,

src/services/UrlScan.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class UrlScanService {
1818
*/
1919
check: async (domain: string) => {
2020
// metrics.increment("services.urlscan.domain.check");
21-
const sanitizedDomain = await sanitizeDomain(domain);
21+
const sanitizedDomain = sanitizeDomain(domain);
2222

2323
const checkSearch = await axios.get(
2424
`https://urlscan.io/api/v1/search/?q=domain:${sanitizedDomain}`,

src/services/VirusTotal.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { headersWithVirusTotal } from "src/defs/headers";
33
import { getDbDomain } from "src/func/db/domain";
44
import { axios } from "src/utils/axios";
55
import { db } from "src/utils/db";
6-
import { sanitizeDomain } from "src/utils/sanitizeDomain";
6+
import { sanitizeDomain, validateDomain, DomainValidationError } from "src/utils/sanitizeDomain";
77

88
/**
99
* A service that provides access to the VirusTotal service for checking and reporting domains.
@@ -19,7 +19,18 @@ export class VirusTotalService {
1919
check: async (domain: string) => {
2020
try {
2121
// metrics.increment("services.virustotal.domain.check");
22-
const sanitizedDomain = await sanitizeDomain(domain);
22+
const sanitizedDomain = sanitizeDomain(domain);
23+
24+
// Validate domain before making API call
25+
try {
26+
await validateDomain(sanitizedDomain);
27+
} catch (error) {
28+
if (error instanceof DomainValidationError) {
29+
console.warn(`VirusTotal: Skipping invalid domain "${domain}": ${error.message}`);
30+
return null;
31+
}
32+
throw error;
33+
}
2334

2435
const response = await axios.get(
2536
`https://www.virustotal.com/api/v3/domains/${sanitizedDomain}`,
@@ -39,9 +50,8 @@ export class VirusTotalService {
3950

4051
return data;
4152
} catch (error) {
42-
// Log the error but don't throw
43-
// FIXME: this is terrible practice, handle ratelimits better after issue addressed.
44-
// console.error(`VirusTotal API error for domain ${domain}:`, error);
53+
// Log the error for transparency, but don't throw to prevent crashes
54+
console.warn(`VirusTotal API error for domain "${domain}":`, error instanceof Error ? error.message : error);
4555
return null;
4656
}
4757
},
@@ -54,7 +64,18 @@ export class VirusTotalService {
5464
*/
5565
report: async (domain: string) => {
5666
// metrics.increment("services.virustotal.domain.report");
57-
const sanitizedDomain = await sanitizeDomain(domain);
67+
const sanitizedDomain = sanitizeDomain(domain);
68+
69+
// Validate domain before making API call
70+
try {
71+
await validateDomain(sanitizedDomain);
72+
} catch (error) {
73+
if (error instanceof DomainValidationError) {
74+
console.warn(`VirusTotal: Skipping report for invalid domain "${domain}": ${error.message}`);
75+
return;
76+
}
77+
throw error;
78+
}
5879

5980
const commentData = {
6081
data: {

src/services/Walshy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class WalshyService {
1818
*/
1919
check: async (domain: string) => {
2020
// metrics.increment("services.walshy.domain.check");
21-
const sanitizedDomain = await sanitizeDomain(domain);
21+
const sanitizedDomain = sanitizeDomain(domain);
2222

2323
const response = await axios.post<{
2424
badDomain: boolean;
@@ -49,7 +49,7 @@ export class WalshyService {
4949
report: async (domain: string) => {
5050
// metrics.increment("services.walshy.domain.report");
5151

52-
const sanitizedDomain = await sanitizeDomain(domain);
52+
const sanitizedDomain = sanitizeDomain(domain);
5353

5454
const response = await axios.post(
5555
`https://bad-domains.walshy.dev/report`,

0 commit comments

Comments
 (0)