Skip to content

Commit

Permalink
add domain report functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jaspermayone committed Dec 28, 2024
1 parent 8f27dd9 commit ae3dc89
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 8 deletions.
21 changes: 17 additions & 4 deletions src/functions/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
/**
* Check the domain against all the services
* @param domain - Domain to check
* @param dbDomain - Domain from the database
* @returns void
*/
export async function domainCheck(domain: string) {
Expand All @@ -27,9 +26,8 @@ export async function domainCheck(domain: string) {

let walshyData = await walshyService.domain.check(domain);
let ipQualityScoreData = await ipQualityScoreService.domain.check(domain);
let googleSafebrowsingData = await googleSafebrowsingService.domain.check(
domain
);
let googleSafebrowsingData =
await googleSafebrowsingService.domain.check(domain);
let sinkingYahtsData = await sinkingYahtsService.domain.check(domain);
let virusTotalData = await virusTotalService.domain.check(domain);
let phishermanData = await phishermanService.domain.check(domain);
Expand Down Expand Up @@ -97,3 +95,18 @@ export async function domainCheck(domain: string) {
phishreportData,
};
}

/**
* Report the domain to all services that support reporting
@param domain
@returns void
*/
export async function domainReport(domain: string) {
let virustotaldata = await virusTotalService.domain.report(domain);
let walshydata = await walshyService.domain.report(domain);

return {
virustotaldata,
walshydata,
};
}
120 changes: 120 additions & 0 deletions src/routes/admin/routes/domain.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import express, { Request, Response } from "express";

import { getUserInfo } from "../../../functions/jwt";
import { prisma } from "../../../prisma";
import { domainReport } from "../../../functions/domain";

/*
GET domain - Get all domains
Expand All @@ -9,6 +11,11 @@ GET domain:id - Get domain by ID

const router = express.Router();

// Add type for report review request
interface ReportReviewRequest {
approved: boolean;
}

/**
* GET /admin/domain
* @summary Returns a list of all domains.
Expand Down Expand Up @@ -65,4 +72,117 @@ router.get("/:id", async (req: Request, res: Response) => {
}
});

/**
* GET /admin/domain/reports
* @summary Get pending domain reports
* @tags Domain - Domain Ops
* @security BearerAuth
*/
router.get("/reports", async (req: Request, res: Response) => {
try {
const reports = await prisma.domainReport.findMany({
where: { status: "PENDING" },
include: {
domain: true,
reporter: {
select: {
id: true,
name: true,
email: true,
},
},
},
});

res.status(200).json(reports);
} catch (error) {
res
.status(500)
.json({ error: "An error occurred while fetching reports." });
}
});

/**
* POST /admin/domain/reports/:id/review
* @summary Review a domain report
* @tags Domain - Domain Ops
* @security BearerAuth
*/
router.post(
"/reports/:id/review",
async (
req: Request<{ id: string }, {}, ReportReviewRequest>,
res: Response,
) => {
try {
const { id } = req.params;
const { approved } = req.body;

if (typeof approved !== "boolean") {
return res
.status(400)
.json({ error: "approved must be a boolean value" });
}

if (!id || isNaN(parseInt(id))) {
return res.status(400).json({ error: "Invalid report ID" });
}

const user = await getUserInfo(prisma, res, req);
if (!user) {
return res.status(401).json({ error: "Unauthorized" });
}

const report = await prisma.domainReport.findUnique({
where: { id: parseInt(id) },
include: { domain: true },
});

if (!report) {
return res.status(404).json({ error: "Report not found" });
}

try {
await prisma.$transaction(async (tx) => {
// Update report status
await tx.domainReport.update({
where: { id: report.id },
data: {
status: approved ? "APPROVED" : "REJECTED",
reviewedAt: new Date(),
reviewer: { connect: { id: user.id } },
},
});

// If approved, mark domain as malicious and run domainReport
if (approved) {
await tx.domain.update({
where: { id: report.domain.id },
data: { malicious: true },
});

// Run domainReport function
await domainReport(report.domain.domain);
}
});

return res
.status(200)
.json(`Report ${approved ? "approved" : "rejected"}`);
} catch (txError) {
console.error("Transaction failed:", txError);
return res.status(500).json({
error: "Failed to process report review",
details: txError instanceof Error ? txError.message : "Unknown error",
});
}
} catch (error) {
console.error("Error in report review endpoint:", error);
return res
.status(500)
.json({ error: "An error occurred while reviewing the report." });
}
},
);

export default router;
69 changes: 65 additions & 4 deletions src/routes/domain.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as express from "express";
import * as jwt from "jsonwebtoken";

import { domainCheck } from "../functions/domain";
import { authenticateToken } from "../functions/jwt";
import { getDbDomain } from "../functions/db/getDbDomain";
import { domainCheck, domainReport } from "../functions/domain";
import { authenticateToken, getUserInfo } from "../functions/jwt";
import { parseData } from "../functions/parseData";
import { logRequest } from "../middleware/logRequest";
import { stripeMeter } from "../middleware/stripeMeter";
Expand Down Expand Up @@ -74,7 +75,7 @@ router.get("/check", authenticateToken, stripeMeter, async (req, res) => {
return res
.status(400)
.json(
"Invalid domain parameter, should be a top level domain. Ex: google.com, amazon.com"
"Invalid domain parameter, should be a top level domain. Ex: google.com, amazon.com",
);
}

Expand Down Expand Up @@ -114,7 +115,7 @@ router.get("/check", authenticateToken, stripeMeter, async (req, res) => {
phishObserverData,
urlScanData,
securitytrailsData,
phishreportData
phishreportData,
);

if (isPhish) {
Expand Down Expand Up @@ -308,4 +309,64 @@ router.put("/classify", authenticateToken, stripeMeter, async (req, res) => {
}
});

/**
* POST /domain/report
* @summary Report a domain as malicious
* @description Reports a domain as potentially malicious. Submissions will be reviewed by a trusted user before being marked and submitted into our database.
* @tags Domain - Endpoints for checking / reporting domains.
* @security BearerAuth
* @param {DomainReport} request.body.required - Domain report details
* @return {object} 200 - Success message
* @return {string} 400 - Error message
* @example request - Domain report
* {
* "domain": "malicious-site.com",
* "notes": "This site is attempting to steal banking credentials"
* }
*/
router.post("/report", authenticateToken, stripeMeter, async (req, res) => {
const { domain, notes } = req.body;
const user = await getUserInfo(prisma, res, req);

if (!domain) {
return res.status(400).json("No domain parameter found");
}

// Get or create domain record
let dbDomain = await getDbDomain(domain);

// Create report
const report = await prisma.domainReport.create({
data: {
domain: { connect: { id: dbDomain.id } },
reporter: { connect: { id: user.id } },
notes,
},
});

// If user is trusted/admin, automatically approve and mark domain as malicious
if (user.permission === "trusted" || user.permission === "admin") {
await prisma.$transaction([
prisma.domainReport.update({
where: { id: report.id },
data: {
status: "APPROVED",
reviewedAt: new Date(),
reviewer: { connect: { id: user.id } },
},
}),
prisma.domain.update({
where: { id: dbDomain.id },
data: { malicious: true },
}),
]);

await domainReport(domain);

return res.status(200).json("Domain reported and marked as malicious");
}

return res.status(200).json("Domain report submitted for review");
});

export default router;

1 comment on commit ae3dc89

@jaspermayone
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Began work on #3

Please sign in to comment.