mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 16:28:11 +00:00
update risk status and update email notifications
This commit is contained in:
@@ -5,7 +5,7 @@ import { Types } from "mongoose";
|
|||||||
import { UnauthorizedRequestError } from "../../utils/errors";
|
import { UnauthorizedRequestError } from "../../utils/errors";
|
||||||
import GitAppOrganizationInstallation from "../../models/gitAppOrganizationInstallation";
|
import GitAppOrganizationInstallation from "../../models/gitAppOrganizationInstallation";
|
||||||
import { MembershipOrg } from "../../models";
|
import { MembershipOrg } from "../../models";
|
||||||
import GitRisks, { STATUS_UNRESOLVED } from "../../models/gitRisks";
|
import GitRisks, { STATUS_RESOLVED_FALSE_POSITIVE, STATUS_RESOLVED_NOT_REVOKED, STATUS_RESOLVED_REVOKED } from "../../models/gitRisks";
|
||||||
|
|
||||||
export const createInstallationSession = async (req: Request, res: Response) => {
|
export const createInstallationSession = async (req: Request, res: Response) => {
|
||||||
const sessionId = crypto.randomBytes(16).toString("hex");
|
const sessionId = crypto.randomBytes(16).toString("hex");
|
||||||
@@ -72,7 +72,7 @@ export const getCurrentOrganizationInstallationStatus = async (req: Request, res
|
|||||||
|
|
||||||
export const getRisksForOrganization = async (req: Request, res: Response) => {
|
export const getRisksForOrganization = async (req: Request, res: Response) => {
|
||||||
const { organizationId } = req.params
|
const { organizationId } = req.params
|
||||||
const risks = await GitRisks.find({ organization: organizationId, status: STATUS_UNRESOLVED }).sort({ createdAt: -1 }).lean()
|
const risks = await GitRisks.find({ organization: organizationId }).sort({ createdAt: -1 }).lean()
|
||||||
res.json({
|
res.json({
|
||||||
risks: risks
|
risks: risks
|
||||||
})
|
})
|
||||||
@@ -81,9 +81,11 @@ export const getRisksForOrganization = async (req: Request, res: Response) => {
|
|||||||
export const updateRisksStatus = async (req: Request, res: Response) => {
|
export const updateRisksStatus = async (req: Request, res: Response) => {
|
||||||
const { riskId } = req.params
|
const { riskId } = req.params
|
||||||
const { status } = req.body
|
const { status } = req.body
|
||||||
const risks = await GitRisks.findByIdAndUpdate(riskId, {
|
const isRiskResolved = status == STATUS_RESOLVED_FALSE_POSITIVE || status == STATUS_RESOLVED_REVOKED || status == STATUS_RESOLVED_NOT_REVOKED ? true : false
|
||||||
sttaus: status
|
const risk = await GitRisks.findByIdAndUpdate(riskId, {
|
||||||
|
status: status,
|
||||||
|
isResolved: isRiskResolved
|
||||||
}).lean()
|
}).lean()
|
||||||
|
|
||||||
res.json(risks)
|
res.json(risk)
|
||||||
}
|
}
|
||||||
@@ -69,6 +69,7 @@ router.post(
|
|||||||
}),
|
}),
|
||||||
param("organizationId").exists().trim(),
|
param("organizationId").exists().trim(),
|
||||||
param("riskId").exists().trim(),
|
param("riskId").exists().trim(),
|
||||||
|
body("status").exists(),
|
||||||
requireOrganizationAuth({
|
requireOrganizationAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||||
acceptedStatuses: [ACCEPTED],
|
acceptedStatuses: [ACCEPTED],
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { exec } from "child_process";
|
|||||||
import { mkdir, readFile, rm, writeFile } from "fs";
|
import { mkdir, readFile, rm, writeFile } from "fs";
|
||||||
import { tmpdir } from "os";
|
import { tmpdir } from "os";
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import GitRisks, { STATUS_RESOLVED_FALSE_POSITIVE } from "../models/gitRisks";
|
import GitRisks from "../models/gitRisks";
|
||||||
import GitAppOrganizationInstallation from "../models/gitAppOrganizationInstallation";
|
import GitAppOrganizationInstallation from "../models/gitAppOrganizationInstallation";
|
||||||
import MembershipOrg from "../models/membershipOrg";
|
import MembershipOrg from "../models/membershipOrg";
|
||||||
import { ADMIN, OWNER } from "../variables";
|
import { ADMIN, OWNER } from "../variables";
|
||||||
@@ -98,8 +98,6 @@ export default async (app: Probot) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change to update
|
// change to update
|
||||||
const noneFalsePositiveFindings: { [key: string]: SecretMatch; } = {}
|
|
||||||
|
|
||||||
for (const key in allFindingsByFingerprint) {
|
for (const key in allFindingsByFingerprint) {
|
||||||
const risk = await GitRisks.findOneAndUpdate({ fingerprint: allFindingsByFingerprint[key].Fingerprint },
|
const risk = await GitRisks.findOneAndUpdate({ fingerprint: allFindingsByFingerprint[key].Fingerprint },
|
||||||
{
|
{
|
||||||
@@ -111,11 +109,6 @@ export default async (app: Probot) => {
|
|||||||
}, {
|
}, {
|
||||||
upsert: true
|
upsert: true
|
||||||
}).lean()
|
}).lean()
|
||||||
|
|
||||||
if (risk?.status == STATUS_RESOLVED_FALSE_POSITIVE) {
|
|
||||||
noneFalsePositiveFindings[key] = { ...convertKeysToLowercase(allFindingsByFingerprint[key]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// get emails of admins
|
// get emails of admins
|
||||||
const adminsOfWork = await MembershipOrg.find({
|
const adminsOfWork = await MembershipOrg.find({
|
||||||
|
|||||||
@@ -11,9 +11,14 @@
|
|||||||
<h3>Infisical has uncovered {{numberOfSecrets}} secret(s) from your recent push</h3>
|
<h3>Infisical has uncovered {{numberOfSecrets}} secret(s) from your recent push</h3>
|
||||||
<p><a href="https://app.infisical.com/secret-scanning"><strong>View leaked secrets</strong></a></p>
|
<p><a href="https://app.infisical.com/secret-scanning"><strong>View leaked secrets</strong></a></p>
|
||||||
<p>One or more secret leaks have been detected in a recent commit pushed by {{pusher_name}} ({{pusher_email}}). If
|
<p>One or more secret leaks have been detected in a recent commit pushed by {{pusher_name}} ({{pusher_email}}). If
|
||||||
the secrets are test secrets, please mark them as false positives in the <a
|
these are test secrets, please add `infisical-scan:ignore` at the end of the line containing the secret as comment
|
||||||
href="https://app.infisical.com/">Infisical dashboard</a>.
|
in the given programming. This will prevent future notifications from being sent out for the given secret(s).</p>
|
||||||
Otherwise, please rotate the secrets immediately.</p>
|
|
||||||
|
<p>If these are production secrets, please rotate them immediately.</p>
|
||||||
|
|
||||||
|
<p>Once you have taken action, be sure to update the status of the risk in your<a
|
||||||
|
href="https://app.infisical.com/">Infisical
|
||||||
|
dashboard</a>.</p>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||||
|
|
||||||
export type GitRisks = {
|
export type GitRisks = {
|
||||||
id: string;
|
_id: string;
|
||||||
description: string;
|
description: string;
|
||||||
startLine: string;
|
startLine: string;
|
||||||
endLine: string;
|
endLine: string;
|
||||||
@@ -20,6 +20,7 @@ export type GitRisks = {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
ruleID: string;
|
ruleID: string;
|
||||||
fingerprint: string;
|
fingerprint: string;
|
||||||
|
status: string;
|
||||||
|
|
||||||
isFalsePositive: boolean; // New field for marking risks as false positives
|
isFalsePositive: boolean; // New field for marking risks as false positives
|
||||||
isResolved: boolean; // New field for marking risks as resolved
|
isResolved: boolean; // New field for marking risks as resolved
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import updateRiskStatus, { RiskStatus } from "@app/pages/api/secret-scanning/updateRiskStatus";
|
||||||
|
|
||||||
|
export const RiskStatusSelection = ({riskId, currentSelection}: {riskId: any, currentSelection: any }) => {
|
||||||
|
const [selectedRiskStatus, setSelectedRiskStatus] = useState(currentSelection);
|
||||||
|
useEffect(()=>{
|
||||||
|
if (currentSelection !== selectedRiskStatus){
|
||||||
|
const updateSelection = async () =>{
|
||||||
|
await updateRiskStatus(String(localStorage.getItem("orgData.id")), riskId, selectedRiskStatus)
|
||||||
|
}
|
||||||
|
updateSelection()
|
||||||
|
}
|
||||||
|
},[selectedRiskStatus])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
value={selectedRiskStatus}
|
||||||
|
onChange={(e) => setSelectedRiskStatus(e.target.value)}
|
||||||
|
className="block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
>
|
||||||
|
<option>Unresolved</option>
|
||||||
|
<option value={RiskStatus.RESOLVED_FALSE_POSITIVE}>This is a false positive</option>
|
||||||
|
<option value={RiskStatus.RESOLVED_REVOKED}>I have rotated the secret, resolve risk</option>
|
||||||
|
<option value={RiskStatus.RESOLVED_NOT_REVOKED}>No rotate needed, resolve</option>
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,17 +11,14 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
THead,
|
THead,
|
||||||
Tr} from "@app/components/v2";
|
Tr} from "@app/components/v2";
|
||||||
|
import timeSince from "@app/ee/utilities/timeSince";
|
||||||
import getRisksByOrganization, { GitRisks } from "@app/pages/api/secret-scanning/getRisksByOrganization";
|
import getRisksByOrganization, { GitRisks } from "@app/pages/api/secret-scanning/getRisksByOrganization";
|
||||||
import { RiskStatus } from "@app/pages/api/secret-scanning/updateRiskStatus";
|
|
||||||
|
import { RiskStatusSelection } from "./RiskStatusSelection";
|
||||||
|
|
||||||
export const SecretScanningLogsTable = () => {
|
export const SecretScanningLogsTable = () => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [gitRisks, setGitRisks] = useState<GitRisks[]>([]);
|
const [gitRisks, setGitRisks] = useState<GitRisks[]>([]);
|
||||||
const [selectedRiskStatus, setSelectedRiskStatus] = useState("");
|
|
||||||
|
|
||||||
const handleSelectRiskStatusUpdate = (event: any) => {
|
|
||||||
setSelectedRiskStatus(event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchRisks = async () => {
|
const fetchRisks = async () => {
|
||||||
@@ -53,7 +50,7 @@ export const SecretScanningLogsTable = () => {
|
|||||||
{!isLoading && gitRisks && gitRisks?.map((risk) => {
|
{!isLoading && gitRisks && gitRisks?.map((risk) => {
|
||||||
return (
|
return (
|
||||||
<Tr key={risk.ruleID} className="h-10">
|
<Tr key={risk.ruleID} className="h-10">
|
||||||
<Td>{risk.createdAt}</Td>
|
<Td>{timeSince(new Date(risk.createdAt))}</Td>
|
||||||
<Td>{risk.ruleID}</Td>
|
<Td>{risk.ruleID}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<a
|
<a
|
||||||
@@ -79,16 +76,7 @@ export const SecretScanningLogsTable = () => {
|
|||||||
</Td>
|
</Td>
|
||||||
<Td>{risk.isResolved ? "Resolved" : "Needs Attention"}</Td>
|
<Td>{risk.isResolved ? "Resolved" : "Needs Attention"}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<select
|
<RiskStatusSelection riskId={risk._id} currentSelection={risk.status}/>
|
||||||
value={selectedRiskStatus}
|
|
||||||
onChange={handleSelectRiskStatusUpdate}
|
|
||||||
className="block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
|
||||||
>
|
|
||||||
<option>Unresolved</option>
|
|
||||||
<option value={RiskStatus.RESOLVED_FALSE_POSITIVE}>This is a false positive</option>
|
|
||||||
<option value={RiskStatus.RESOLVED_REVOKED}>I have rotated the secret, resolve risk</option>
|
|
||||||
<option value={RiskStatus.RESOLVED_NOT_REVOKED}>No rotate needed, resolve</option>
|
|
||||||
</select>
|
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user