diff --git a/src/main/api/project.ts b/src/main/api/project.ts index 1d248b6..85e7bfb 100644 --- a/src/main/api/project.ts +++ b/src/main/api/project.ts @@ -179,3 +179,14 @@ export function projectScan(projectName: string) { return 'error'; } } + +export function projectAttackResult(projectName: string) { + try { + const db = connectJson( + `${PROJECT_DIR}/${projectName}/general_scanning.json`, + ); + return db.read(); + } catch (error) { + return 'error'; + } +} diff --git a/src/main/api/serve.ts b/src/main/api/serve.ts index 4e4e9cd..cb8ed3b 100644 --- a/src/main/api/serve.ts +++ b/src/main/api/serve.ts @@ -20,7 +20,7 @@ export function returnFile(where: string, type: string) { console.error('Error reading file:', error); return { statusCode: 500, - body: JSON.stringify({ error: 'Internal Server Error' }), + body: 'no results', headers: { 'Content-Type': 'application/json', }, diff --git a/src/main/jsleak/jsleak.ts b/src/main/jsleak/jsleak.ts index 8736120..6430f43 100644 --- a/src/main/jsleak/jsleak.ts +++ b/src/main/jsleak/jsleak.ts @@ -11,7 +11,7 @@ export async function findSecret(outputDir: string = PROJECT_DIR): Promise<{ success: boolean; error: any; }> { - const jsleak = toolPath('jsleak'); + const jsleak = '$(go env GOPATH)/bin/jsleak'; const command = `${CurrentOS() === 'win32' ? 'type' : 'cat'} ${path.join(outputDir, 'httpx_live_domains.txt')} | ${jsleak} -s`; try { fs.writeFileSync( @@ -40,7 +40,7 @@ export async function extraLinks(outputDir: string = PROJECT_DIR): Promise<{ success: boolean; error: any; }> { - const jsleak = toolPath('jsleak'); + const jsleak = '$(go env GOPATH)/bin/jsleak'; const command = `${CurrentOS() === 'win32' ? 'type' : 'cat'} ${path.join(outputDir, 'httpx_live_domains.txt')} | ${jsleak} -l | findstr ".js"`; try { fs.writeFileSync( diff --git a/src/main/main.ts b/src/main/main.ts index 90fad0f..ad00c2c 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -13,6 +13,7 @@ import { app, BrowserWindow, shell, ipcMain } from 'electron'; import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; import os from 'node:os'; +import axios from 'axios'; import MenuBuilder from './menu'; import { initProjectDir, resolveHtmlPath } from './util'; import { subFinder } from './recon/subfinder'; @@ -21,6 +22,7 @@ import { createJsonFile, createProjectDir, createRequestToUrlScanner, + projectAttackResult, projectDetails, projectScan, readDirectoryNames, @@ -190,6 +192,47 @@ ipcMain.handle('create-project', async (event, args) => { } }); +ipcMain.handle('get-attack-result', async (event, args) => { + const projectName = args[0]; + try { + const results = await projectAttackResult(projectName); + return { error: false, results }; + } catch (err) { + return { error: true }; + } +}); + +ipcMain.handle('fetch-data', async (event, args) => { + const url = + 'https://api.cloudflare.com/client/v4/accounts/3cce5a88886b46f56d9ff989b715a588/ai/run/@cf/openchat/openchat-3.5-0106'; + const token = 'YbXmqtPZXLgeQSOSjMHC3ka4Qret1QCpQSZXMWCR'; + + const requestData = { + stream: false, + messages: [ + { role: 'system', content: 'You are a web security consaltunt' }, + { + role: 'user', + content: `can you help me to have attack or prevent this ${args[0].userInput} `, + }, + ], + }; + + try { + const response = await axios.post(url, requestData, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + console.log(response); + return response.data; + } catch (error) { + console.error('Failed to fetch data:', error); + throw error; + } +}); + if (process.env.NODE_ENV === 'production') { const sourceMapSupport = require('source-map-support'); sourceMapSupport.install(); @@ -198,9 +241,9 @@ if (process.env.NODE_ENV === 'production') { const isDebug = process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'; -if (isDebug) { - require('electron-debug')(); -} +// if (isDebug) { +// require('electron-debug')(); +// } const installExtensions = async () => { const installer = require('electron-devtools-installer'); diff --git a/src/main/preload.ts b/src/main/preload.ts index cde12a4..e996022 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -25,8 +25,10 @@ export type Channels = | 'exposures' | 'vulns-cves' | 'lfi' + | 'fetch-data' | 'potential-xss' | 'multi-scans' + | 'get-attack-result' | 'finished-scan'; const electronHandler = { diff --git a/src/main/scanning/dalfox.ts b/src/main/scanning/dalfox.ts index 45eec99..37d6443 100644 --- a/src/main/scanning/dalfox.ts +++ b/src/main/scanning/dalfox.ts @@ -14,12 +14,11 @@ export async function scanningForXSS(outputDir: string = PROJECT_DIR): Promise<{ success: boolean; error: any; }> { - const dalfox = toolPath('dalfox'); - const command = `${dalfox} file ${path.join(outputDir, 'waybackurls_archive.txt')} --skip-bav + const dalfox = 'dalfox'; + const command = `${dalfox} file ${path.join(outputDir, 'httpx_live_domains.txt')} --skip-bav >> ${path.join(outputDir, 'XSS.txt')}`; try { await execAsync(command); - console.log(command); const numberOfUrls = await countLines(path.join(outputDir, 'XSS.txt')); const db = connectJson(path.join(`${outputDir}/details.json`)); await db.update({ @@ -41,8 +40,8 @@ export async function multiScans(outputDir: string = PROJECT_DIR): Promise<{ success: boolean; error: any; }> { - const dalfox = toolPath('dalfox'); - const command = `${dalfox} file ${path.join(outputDir, 'waybackurls_archive.txt')} + const dalfox = 'dalfox'; + const command = `${dalfox} file ${path.join(outputDir, 'httpx_live_domains.txt')} >> ${path.join(outputDir, 'multi_scans.txt')}`; try { await execAsync(command); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 86abb64..dac9a5a 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -11,6 +11,7 @@ import Recon from './views/project/recon'; import { ReconResults } from './views/project/results/recon-results'; import JsLeaks from './views/project/JsLeaks'; import Attacks from './views/project/attacks'; +import AttacksResults from './views/project/attackResults'; function Home() { return ( @@ -33,6 +34,7 @@ export default function App() { } /> } /> } /> + } /> } /> diff --git a/src/renderer/components/ai/aiChat.tsx b/src/renderer/components/ai/aiChat.tsx new file mode 100644 index 0000000..1b4ecda --- /dev/null +++ b/src/renderer/components/ai/aiChat.tsx @@ -0,0 +1,95 @@ +/* eslint-disable no-undef */ +/* eslint-disable import/prefer-default-export */ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable react/button-has-type */ +/* eslint-disable react/function-component-definition */ +import React, { useState, useEffect } from 'react'; + +import { Loader2 } from 'lucide-react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '../ui/dialog'; + +import { Button } from '../ui/button'; + +const UIofAI: React.FC = ({ + closeModal, + input, +}: { + closeModal: Boolean; + input: string; +}) => { + const [messages, setMessages] = useState(''); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchDataFromApi = async () => { + const userInput = input; // Example of serializable data + try { + const response = await window.electron.ipcRenderer.invoke( + 'fetch-data', + { userInput }, + ); + console.log('Received data:', response); + setLoading(false); + setMessages(response.result.response); + } catch (error) { + console.error('Failed to fetch data:', error); + // Handle errors from the main process + } + }; + + fetchDataFromApi(); + }, []); // Empty dependency array ensures useEffect runs only once + + return ( +
+ {loading && ( +
+ +
+ )} +
+

{messages}

+
+ {error &&

Error: {error}

} + +
+ ); +}; + +export const AskAi = ({ + input, + severity, +}: { + input: string; + severity: string; +}) => { + const [isModalOpen, setIsModalOpen] = useState(false); + + const openModal = () => setIsModalOpen(true); + const closeModal = () => setIsModalOpen(false); + + return ( +
+ + + + + + + Getting info about this vulnerability + + + + +
+ ); +}; diff --git a/src/renderer/components/projectCard.tsx b/src/renderer/components/projectCard.tsx index 53b53f4..b4c8e32 100644 --- a/src/renderer/components/projectCard.tsx +++ b/src/renderer/components/projectCard.tsx @@ -33,7 +33,7 @@ export function ProjectCard({ name }: { name: string }) { return ( project && ( - + {project.name} {project.domain} @@ -41,9 +41,6 @@ export function ProjectCard({ name }: { name: string }) {

{formatDistanceToNow(project.updatedAt ?? Date.now())}

- -

Card Footer

-
) diff --git a/src/renderer/components/sidebar.tsx b/src/renderer/components/sidebar.tsx index 63896eb..469f0ff 100644 --- a/src/renderer/components/sidebar.tsx +++ b/src/renderer/components/sidebar.tsx @@ -34,16 +34,10 @@ const dashboardMenu: DashboardMenu[] = [ title: 'Attacks', icon: , }, - { - href: 'interceptor', - title: 'Interceptor', - Disabled: true, - icon: , - }, { href: 'jsleaks', - title: 'JsLeaks', + title: 'JavaScript Leaks', icon: , }, ]; @@ -57,15 +51,8 @@ const resultArch: DashboardMenu[] = [ { href: 'attack-result', title: 'Attack', - Disabled: true, icon: , }, - { - href: 'interceptor-result', - title: 'Interceptor', - Disabled: true, - icon: , - }, ]; export default function SideBar({ project }: SideBarProps) { diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index a64b4be..707c6bc 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -6,10 +6,3 @@ import App from './App'; const container = document.getElementById('root') as HTMLElement; const root = createRoot(container); root.render(); - -// calling IPC exposed from preload script -window.electron.ipcRenderer.once('ipc-example', (arg) => { - // eslint-disable-next-line no-console - console.log(arg); -}); -window.electron.ipcRenderer.sendMessage('ipc-example', ['ping']); diff --git a/src/renderer/lib/ai.ts b/src/renderer/lib/ai.ts new file mode 100644 index 0000000..d6c750d --- /dev/null +++ b/src/renderer/lib/ai.ts @@ -0,0 +1,83 @@ +/* eslint-disable promise/always-return */ +/* eslint-disable import/prefer-default-export */ +/* eslint-disable no-unused-vars */ +const url = + 'https://api.cloudflare.com/client/v4/accounts/3cce5a88886b46f56d9ff989b715a588/ai/run/@cf/openchat/openchat-3.5-0106'; +const token = 'YbXmqtPZXLgeQSOSjMHC3ka4Qret1QCpQSZXMWCR'; + +interface Message { + role: string; + content: string; +} + +interface StreamResponse { + response: string; +} + +export const streamOpenChat = ( + userInput: string, + onMessage: (message: string) => void, + onComplete: () => void, + onError: (error: any) => void, +): void => { + try { + const headers = new Headers(); + headers.append('Content-Type', 'application/json'); + headers.append('Authorization', `Bearer ${token}`); + + const init: RequestInit = { + method: 'POST', + headers, + body: JSON.stringify({ + stream: true, + messages: [ + { role: 'system', content: 'You are a web security consultant' }, + { role: 'user', content: userInput }, + ], + }), + }; + + fetch(url, init) + .then((response) => { + if (!response.body) { + throw new Error('ReadableStream not supported'); + } + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + const readChunk = () => { + reader + .read() + .then(({ done, value }) => { + if (done) { + onComplete(); + return; + } + const text = decoder.decode(value, { stream: true }); + const lines = text.trim().split('\n'); + lines.forEach((line: string) => { + if (line.startsWith('data: ')) { + try { + const jsonData: StreamResponse = JSON.parse(line.slice(6)); + onMessage(jsonData.response); + } catch (error) { + console.error('Failed to parse JSON:', error); + } + } + }); + readChunk(); + }) + .catch((error) => { + onError(error); + }); + }; + + readChunk(); + }) + .catch((error) => { + onError(error); + }); + } catch (error) { + onError(error); + } +}; diff --git a/src/renderer/types.ts b/src/renderer/types.ts index a364e2b..53345f4 100644 --- a/src/renderer/types.ts +++ b/src/renderer/types.ts @@ -25,6 +25,9 @@ export interface ProjectDetails { screens?: JobDetails; params?: JobDetails; liveDomains?: JobDetails; + generalScanning?: JobDetails; + XSS?: JobDetails; + multiScans?: JobDetails; archive?: JobDetails; js?: JobDetails; updatedAt: Date; diff --git a/src/renderer/views/Dashboard.tsx b/src/renderer/views/Dashboard.tsx index 292df53..cc617fe 100644 --- a/src/renderer/views/Dashboard.tsx +++ b/src/renderer/views/Dashboard.tsx @@ -25,10 +25,13 @@ import { import { CreateProjectForm } from '../components/createProject.form'; import { ProjectCard } from '../components/projectCard'; +import { Input } from '../components/ui/input'; // eslint-disable-next-line import/prefer-default-export export function Dashboard() { const [projects, setProjects] = useState(); + const [searchQuery, setSearchQuery] = useState(''); + const list = async () => { try { const projectNames: string[] = @@ -42,26 +45,41 @@ export function Dashboard() { useEffect(() => { list(); }, []); - + const filteredProjects = projects?.filter((project) => + project.toLowerCase().includes(searchQuery.toLowerCase()), + ); return ( -
- {projects?.map((project) => { - return ; - })} - -

New project

- - - - - - - New Project - - - - -
+
+
+

Dashboard

+ setSearchQuery(e.target.value)} + className="p-2 max-w-[18rem] border border-gray-300 rounded" + /> +
+
+ +

New project

+ + + + + + + New Project + + + + +
+ {filteredProjects?.map((project) => ( + + ))} + {filteredProjects?.length === 0 &&
No Results Found
} +
); } diff --git a/src/renderer/views/project/attackResults.tsx b/src/renderer/views/project/attackResults.tsx new file mode 100644 index 0000000..c502dd3 --- /dev/null +++ b/src/renderer/views/project/attackResults.tsx @@ -0,0 +1,282 @@ +/* eslint-disable jsx-a11y/control-has-associated-label */ +/* eslint-disable no-undef */ +/* eslint-disable no-use-before-define */ +/* eslint-disable react/function-component-definition */ +/* eslint-disable react/jsx-props-no-spreading */ + +import { + JSXElementConstructor, + Key, + ReactElement, + ReactNode, + ReactPortal, + useEffect, + useState, +} from 'react'; +import { useParams } from 'react-router-dom'; + +import { ProjectDetails } from '../../types'; +import { AskAi } from '../../components/ai/aiChat'; + +const records = [ + { + 'template-id': 'xss-deprecated-header', + 'template-path': + '/Users/kroking/nuclei-templates/http/misconfiguration/xss-deprecated-header.yaml', + info: { + name: 'XSS-Protection Header - Cross-Site Scripting', + author: ['joshlarsen'], + tags: ['xss', 'misconfig', 'generic'], + description: + 'Setting the XSS-Protection header is deprecated. Setting the header to anything other than `0` can actually introduce an XSS vulnerability.', + reference: [ + 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection', + 'https://owasp.org/www-project-secure-headers/#x-xss-protection', + ], + severity: 'info', + metadata: { 'max-request': 1 }, + classification: { + 'cve-id': null, + 'cwe-id': null, + 'cvss-metrics': 'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N', + }, + }, + type: 'http', + host: 'hi.new', + port: '443', + scheme: 'https', + url: 'https://hi.new', + 'matched-at': 'https://hi.new', + 'extracted-results': ['1; mode=block'], + ip: '172.67.133.192', + timestamp: '2024-06-22T03:54:22.157998+03:00', + 'curl-command': + "curl -X 'GET' -H 'Accept: */*' -H 'Accept-Language: en' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0' 'https://hi.new'", + 'matcher-status': true, + }, + { + 'template-id': 'xss-deprecated-header', + 'template-path': + '/Users/kroking/nuclei-templates/http/misconfiguration/xss-deprecated-header.yaml', + info: { + name: 'XSS-Protection Header - Cross-Site Scripting', + author: ['joshlarsen'], + tags: ['xss', 'misconfig', 'generic'], + description: + 'Setting the XSS-Protection header is deprecated. Setting the header to anything other than `0` can actually introduce an XSS vulnerability.', + reference: [ + 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection', + 'https://owasp.org/www-project-secure-headers/#x-xss-protection', + ], + severity: 'info', + metadata: { + 'max-request': 1, + }, + classification: { + 'cve-id': null, + 'cwe-id': null, + 'cvss-metrics': 'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N', + }, + }, + type: 'http', + host: 'api.hi.new', + port: '443', + scheme: 'https', + url: 'https://api.hi.new', + 'matched-at': 'https://api.hi.new', + 'extracted-results': ['1; mode=block'], + request: + 'GET / HTTP/1.1\r\nHost: api.hi.new\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62\r\nConnection: close\r\nAccept: */*\r\nAccept-Language: en\r\nAccept-Encoding: gzip\r\n\r\n', + response: + 'HTTP/1.1 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nAccess-Control-Allow-Credentials: true\r\nAlt-Svc: h3=":443"; ma=86400\r\nCf-Cache-Status: DYNAMIC\r\nCf-Ray: 8978444758782a5b-CDG\r\nContent-Security-Policy: default-src https: data: \'unsafe-inline\' \'unsafe-eval\'\r\nContent-Type: text/html; charset=utf-8\r\nDate: Sat, 22 Jun 2024 00:54:22 GMT\r\nNel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nReport-To: {"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=%2F5Id2ZLinpC37qAamn%2B7RiNq1yw9dNzmUAkXBUOK61mKhOJHY0XfC0ts1OG8qtiLLqpIUAqs1e6Dc38rl389H500ip6MGLIowF3iW3RlFN2ibEwNQ5gUL2GvUtXg"}],"group":"cf-nel","max_age":604800}\r\nServer: cloudflare\r\nStrict-Transport-Security: max-age=31536000; includeSubdomains\r\nVary: Origin\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: SAMEORIGIN\r\nX-Powered-By: Express\r\nX-Ratelimit-Limit: 30\r\nX-Ratelimit-Remaining: 17\r\nX-Ratelimit-Reset: 31\r\nX-Xss-Protection: 1; mode=block\r\n\r\nHello World!', + ip: '104.21.14.16', + timestamp: '2024-06-22T03:54:22.511454+03:00', + 'curl-command': + "curl -X 'GET' -H 'Accept: */*' -H 'Accept-Language: en' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62' 'https://api.hi.new'", + 'matcher-status': true, + }, + { + 'template-id': 'tech-detect', + 'template-path': + '/Users/kroking/nuclei-templates/http/technologies/tech-detect.yaml', + info: { + name: 'Wappalyzer Technology Detection', + author: ['hakluke', 'righettod'], + tags: ['tech'], + severity: 'info', + metadata: { + 'max-request': 1, + }, + }, + 'matcher-name': 'express', + type: 'http', + host: 'api.hi.new', + port: '443', + scheme: 'https', + url: 'https://api.hi.new', + 'matched-at': 'https://api.hi.new', + request: + 'GET / HTTP/1.1\r\nHost: api.hi.new\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/607.1.40 (KHTML, like Gecko) Version/9.1.2 Safari/607.1.40\r\nConnection: close\r\nAccept: */*\r\nAccept-Language: en\r\nAccept-Encoding: gzip\r\n\r\n', + response: + 'HTTP/1.1 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nAccess-Control-Allow-Credentials: true\r\nAlt-Svc: h3=":443"; ma=86400\r\nCf-Cache-Status: DYNAMIC\r\nCf-Ray: 897848814b9c0da6-MRS\r\nContent-Security-Policy: default-src https: data: \'unsafe-inline\' \'unsafe-eval\'\r\nContent-Type: text/html; charset=utf-8\r\nDate: Sat, 22 Jun 2024 00:57:15 GMT\r\nNel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nReport-To: {"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=6cObOX6LAj1mkw3EEpsJmtuNyJ88sNJmWoORqzXem1bOYB6uSy0sY9GW8dMxkeVuSTxzTKK%2FxTEZ0YAfgBDmgji%2BGkmPZb%2BxjoPSeBZn7a4NIJXNmN3QaExSZWic"}],"group":"cf-nel","max_age":604800}\r\nServer: cloudflare\r\nStrict-Transport-Security: max-age=31536000; includeSubdomains\r\nVary: Origin\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: SAMEORIGIN\r\nX-Powered-By: Express\r\nX-Ratelimit-Limit: 30\r\nX-Ratelimit-Remaining: 25\r\nX-Ratelimit-Reset: 43\r\nX-Xss-Protection: 1; mode=block\r\n\r\nHello World!', + ip: '104.21.14.16', + timestamp: '2024-06-22T03:57:15.684221+03:00', + 'curl-command': + "curl -X 'GET' -H 'Accept: */*' -H 'Accept-Language: en' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/607.1.40 (KHTML, like Gecko) Version/9.1.2 Safari/607.1.40' 'https://api.hi.new'", + 'matcher-status': true, + }, + { + 'template-id': 'tech-detect', + 'template-path': + '/Users/kroking/nuclei-templates/http/technologies/tech-detect.yaml', + info: { + name: 'Wappalyzer Technology Detection', + author: ['hakluke', 'righettod'], + tags: ['tech'], + severity: 'info', + metadata: { + 'max-request': 1, + }, + }, + 'matcher-name': 'cloudflare', + type: 'http', + host: 'api.hi.new', + port: '443', + scheme: 'https', + url: 'https://api.hi.new', + 'matched-at': 'https://api.hi.new', + request: + 'GET / HTTP/1.1\r\nHost: api.hi.new\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/607.1.40 (KHTML, like Gecko) Version/9.1.2 Safari/607.1.40\r\nConnection: close\r\nAccept: */*\r\nAccept-Language: en\r\nAccept-Encoding: gzip\r\n\r\n', + response: + 'HTTP/1.1 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\nAccess-Control-Allow-Credentials: true\r\nAlt-Svc: h3=":443"; ma=86400\r\nCf-Cache-Status: DYNAMIC\r\nCf-Ray: 897848814b9c0da6-MRS\r\nContent-Security-Policy: default-src https: data: \'unsafe-inline\' \'unsafe-eval\'\r\nContent-Type: text/html; charset=utf-8\r\nDate: Sat, 22 Jun 2024 00:57:15 GMT\r\nNel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nReport-To: {"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=6cObOX6LAj1mkw3EEpsJmtuNyJ88sNJmWoORqzXem1bOYB6uSy0sY9GW8dMxkeVuSTxzTKK%2FxTEZ0YAfgBDmgji%2BGkmPZb%2BxjoPSeBZn7a4NIJXNmN3QaExSZWic"}],"group":"cf-nel","max_age":604800}\r\nServer: cloudflare\r\nStrict-Transport-Security: max-age=31536000; includeSubdomains\r\nVary: Origin\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: SAMEORIGIN\r\nX-Powered-By: Express\r\nX-Ratelimit-Limit: 30\r\nX-Ratelimit-Remaining: 25\r\nX-Ratelimit-Reset: 43\r\nX-Xss-Protection: 1; mode=block\r\n\r\nHello World!', + ip: '104.21.14.16', + timestamp: '2024-06-22T03:57:15.68423+03:00', + 'curl-command': + "curl -X 'GET' -H 'Accept: */*' -H 'Accept-Language: en' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/607.1.40 (KHTML, like Gecko) Version/9.1.2 Safari/607.1.40' 'https://api.hi.new'", + 'matcher-status': true, + }, +]; + +interface RecordInfo { + name: string; + description: string; + severity: string; +} + +interface Record { + 'template-id': string; + info: RecordInfo; + url: string; +} + +const severityColors = { + info: 'bg-blue-100 text-blue-800', + warning: 'bg-yellow-100 text-yellow-800', + high: 'bg-red-100 text-red-800', + critical: 'bg-purple-100 text-purple-800', +}; + +const groupByUrl = (records: Record[]) => { + return records.reduce((acc, record) => { + const { url } = record; + if (!acc[url]) { + acc[url] = []; + } + acc[url].push({ + templateId: record['template-id'], + name: record.info.name, + description: record.info.description, + severity: record.info.severity, + }); + return acc; + }, {} as RecordGroupedByURL); +}; + +interface RecordTableProps { + data: RecordGroupedByURL; +} + +const RecordTable: React.FC = ({ data }) => { + return ( +
+ {Object.keys(data).map((url) => ( +
+

{url}

+
+ + + + + + + + + + + + {data[url].map((record, index) => ( + + + + + + + + ))} + +
Template IDNameDescriptionAsk AISeverity
{record.templateId}{record.name}{record.description} + {record.description && ( + + )} + + {record.severity} +
+
+
+ ))} +
+ ); +}; +type RecordGroupedByURL = { [url: string]: RecordInfo[] }; + +const AttacksResults: React.FC = () => { + const [details, setDetails] = useState(); + const { projectSlug } = useParams<{ projectSlug: string }>(); + + useEffect(() => { + const getDetails = async () => { + try { + const res = await window.electron.ipcRenderer.invoke( + 'get-attack-result', + projectSlug, + ); + const groupedData = groupByUrl(res.results); + setDetails(groupedData); + } catch (error) { + console.error('Error fetching attack results:', error); + // Handle error state or show error message + } + }; + getDetails(); + }, [projectSlug]); + + if (!details) { + return
Loading...
; + } + + return ( +
+

Attack Results

+ +
+ ); +}; + +export default AttacksResults; diff --git a/src/renderer/views/project/status.tsx b/src/renderer/views/project/status.tsx index 41f11b5..135aa9e 100644 --- a/src/renderer/views/project/status.tsx +++ b/src/renderer/views/project/status.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/jsx-no-comment-textnodes */ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable react/jsx-no-useless-fragment */ /* eslint-disable react-hooks/exhaustive-deps */ @@ -94,7 +95,7 @@ export default function Status() {
)}

Insights

-
+
{project && }