mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 18:15:53 +00:00
feat: renamed old frontend and fixed some minor bugs
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import { useEffect } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useSearch } from "@tanstack/react-router";
|
||||
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button, NoticeBanner } from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
useServerConfig
|
||||
} from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import {
|
||||
useCreateNewInstallationSession,
|
||||
useGetSecretScanningInstallationStatus,
|
||||
useLinkGitAppInstallationWithOrg
|
||||
} from "@app/hooks/api/secretScanning";
|
||||
|
||||
import { SecretScanningLogsTable } from "./components";
|
||||
|
||||
export const SecretScanningPage = withPermission(
|
||||
() => {
|
||||
const queryParams = useSearch({
|
||||
from: ROUTE_PATHS.Organization.SecretScanning.id
|
||||
});
|
||||
const { config } = useServerConfig();
|
||||
const { currentOrg } = useOrganization();
|
||||
const organizationId = currentOrg.id;
|
||||
|
||||
const { mutateAsync: linkGitAppInstallationWithOrganization } =
|
||||
useLinkGitAppInstallationWithOrg();
|
||||
const { mutateAsync: createNewIntegrationSession } = useCreateNewInstallationSession();
|
||||
const { data: installationStatus, isPending: isSecretScanningInstatllationStatusLoading } =
|
||||
useGetSecretScanningInstallationStatus(organizationId);
|
||||
const integrationEnabled =
|
||||
!isSecretScanningInstatllationStatusLoading && installationStatus?.appInstallationCompleted;
|
||||
|
||||
useEffect(() => {
|
||||
const linkInstallation = async () => {
|
||||
if (queryParams.state && queryParams.installation_id) {
|
||||
try {
|
||||
const isLinked = await linkGitAppInstallationWithOrganization({
|
||||
installationId: queryParams.installation_id as string,
|
||||
sessionId: queryParams.state as string
|
||||
});
|
||||
if (isLinked) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
console.log("installation verification complete");
|
||||
} catch (e) {
|
||||
console.log("app installation is stale, start new session", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
linkInstallation();
|
||||
}, [queryParams.state, queryParams.installation_id]);
|
||||
|
||||
const generateNewIntegrationSession = async () => {
|
||||
const session = await createNewIntegrationSession({ organizationId });
|
||||
window.location.href = `https://github.com/apps/infisical-radar/installations/new?state=${session.sessionId}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Secret scanning</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
<meta property="og:image" content="/images/message.png" />
|
||||
</Helmet>
|
||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
||||
<div className="w-full max-w-7xl px-6">
|
||||
<div className="mt-6 text-3xl font-semibold text-gray-200">Secret Scanning</div>
|
||||
<div className="mb-6 text-lg text-mineshaft-300">
|
||||
Automatically monitor your GitHub activity and prevent secret leaks
|
||||
</div>
|
||||
{config.isSecretScanningDisabled && (
|
||||
<NoticeBanner title="Secret scanning is in maintenance" className="mb-4">
|
||||
We are working on improving the performance of secret scanning due to increased
|
||||
usage.
|
||||
</NoticeBanner>
|
||||
)}
|
||||
<div className="relative mb-6 flex justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6">
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="mb-1 flex flex-row">
|
||||
Secret Scanning Status:{" "}
|
||||
{integrationEnabled ? (
|
||||
<p className="ml-1.5 font-semibold text-green">Enabled</p>
|
||||
) : (
|
||||
<p className="ml-1.5 font-semibold text-red">Not enabled</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{integrationEnabled ? (
|
||||
<p className="text-mineshaft-300">
|
||||
Your GitHub organization is connected to Infisical, and is being continuously
|
||||
monitored for secret leaks.
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-mineshaft-300">
|
||||
Connect your GitHub organization to Infisical.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{integrationEnabled ? (
|
||||
<div>
|
||||
<div className="absolute right-[2.5rem] top-[2.5rem] flex h-6 w-6 animate-ping items-center justify-center rounded-full bg-green" />
|
||||
<div className="absolute right-[2.63rem] top-[2.63rem] flex h-5 w-5 animate-ping items-center justify-center rounded-full bg-green" />
|
||||
<div className="absolute right-[2.82rem] top-[2.82rem] flex h-3.5 w-3.5 animate-ping items-center justify-center rounded-full bg-green" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-[3.25rem] items-center">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Create}
|
||||
a={OrgPermissionSubjects.SecretScanning}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
variant="solid"
|
||||
colorSchema="primary"
|
||||
onClick={generateNewIntegrationSession}
|
||||
className="h-min py-2"
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Integrate with GitHub
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<SecretScanningLogsTable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{
|
||||
action: OrgPermissionActions.Read,
|
||||
subject: OrgPermissionSubjects.SecretScanning
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,46 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { useUpdateRiskStatus } from "@app/hooks/api/secretScanning";
|
||||
import { RiskStatus } from "@app/hooks/api/secretScanning/types";
|
||||
|
||||
export const RiskStatusSelection = ({
|
||||
riskId,
|
||||
currentSelection
|
||||
}: {
|
||||
riskId: any;
|
||||
currentSelection: any;
|
||||
}) => {
|
||||
const [selectedRiskStatus, setSelectedRiskStatus] = useState(currentSelection);
|
||||
const { mutate: updateRiskStatus } = useUpdateRiskStatus();
|
||||
const { currentOrg } = useOrganization();
|
||||
const organizationId = currentOrg.id;
|
||||
|
||||
return (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.SecretScanning}>
|
||||
{(isAllowed) => (
|
||||
<select
|
||||
disabled={!isAllowed}
|
||||
value={selectedRiskStatus}
|
||||
onChange={(e) => {
|
||||
setSelectedRiskStatus(e.target.value);
|
||||
updateRiskStatus({
|
||||
organizationId,
|
||||
status: e.target.value as RiskStatus,
|
||||
riskId
|
||||
});
|
||||
}}
|
||||
className="block w-full rounded-md px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-blue-500"
|
||||
>
|
||||
<option>Unresolved</option>
|
||||
<option value={RiskStatus.RESOLVED_FALSE_POSITIVE}>
|
||||
This is a false positive, resolved
|
||||
</option>
|
||||
<option value={RiskStatus.RESOLVED_REVOKED}>I have rotated the secret, resolved</option>
|
||||
<option value={RiskStatus.RESOLVED_NOT_REVOKED}>No rotate needed, resolved</option>
|
||||
</select>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
);
|
||||
};
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
import { faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { formatDistance } from "date-fns";
|
||||
|
||||
import {
|
||||
EmptyState,
|
||||
Table,
|
||||
TableContainer,
|
||||
TableSkeleton,
|
||||
TBody,
|
||||
Td,
|
||||
Th,
|
||||
THead,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import { useGetSecretScanningRisks } from "@app/hooks/api/secretScanning";
|
||||
|
||||
import { RiskStatusSelection } from "./RiskStatusSelection";
|
||||
|
||||
export const SecretScanningLogsTable = () => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const organizationId = currentOrg.id;
|
||||
const { isPending, data: gitRisks } = useGetSecretScanningRisks(organizationId);
|
||||
|
||||
return (
|
||||
<TableContainer className="mt-8">
|
||||
<Table>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th className="flex-1">Date</Th>
|
||||
<Th className="flex-1">Secret Type</Th>
|
||||
<Th className="flex-1">View Risk</Th>
|
||||
<Th className="flex-1">Info</Th>
|
||||
<Th className="flex-1">Status</Th>
|
||||
<Th className="flex-1">Action</Th>
|
||||
<Th className="w-5" />
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{!isPending &&
|
||||
gitRisks &&
|
||||
gitRisks?.map((risk) => {
|
||||
return (
|
||||
<Tr key={risk.ruleID} className="h-10">
|
||||
<Td>{formatDistance(new Date(risk.createdAt), new Date())}</Td>
|
||||
<Td>{risk.ruleID}</Td>
|
||||
<Td>
|
||||
<a
|
||||
href={`https://github.com/${risk.repositoryFullName}/blob/${risk.commit}/${risk.file}#L${risk.startLine}-L${risk.endLine}`}
|
||||
target="_blank"
|
||||
className="text-red-500"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View Exposed Secret
|
||||
</a>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="font-bold">
|
||||
<a href={`https://github.com/${risk.repositoryFullName}`}>
|
||||
{risk.repositoryFullName}
|
||||
</a>
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
<span>{risk.file}</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className="font-bold">{risk.author}</span>
|
||||
<br />
|
||||
<span>{risk.email}</span>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{risk.isResolved ? "Resolved" : "Needs Attention"}</Td>
|
||||
<Td>
|
||||
<RiskStatusSelection riskId={risk.id} currentSelection={risk.status} />
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
{isPending && <TableSkeleton columns={7} innerKey="gitRisks" />}
|
||||
{!isPending && gitRisks && gitRisks?.length === 0 && (
|
||||
<Tr>
|
||||
<Td colSpan={7}>
|
||||
<EmptyState title="No risks detected." icon={faCheck} />
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
</TBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { SecretScanningLogsTable } from "./SecretScanningLogsTable";
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createFileRoute, stripSearchParams } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretScanningPage } from "./SecretScanningPage";
|
||||
|
||||
const SecretScanningQueryParams = z.object({
|
||||
state: z.string().catch(""),
|
||||
installation_id: z.string().catch("")
|
||||
});
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/organization/_layout/secret-scanning"
|
||||
)({
|
||||
component: SecretScanningPage,
|
||||
validateSearch: zodValidator(SecretScanningQueryParams),
|
||||
search: {
|
||||
middlewares: [
|
||||
stripSearchParams({
|
||||
installation_id: "",
|
||||
state: ""
|
||||
})
|
||||
]
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user