mirror of
https://github.com/msoedov/agentic_security.git
synced 2026-08-09 03:06:05 +02:00
migration to vueCLi and css to tailwind css 3 done
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
VUE_APP_SERVER_URL=''#replace this with url at which agentic_security server is running
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
node :true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:vue/essential',
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 12,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'vue',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': 'off', // Disable the rule
|
||||||
|
'no-constant-condition': 'off',
|
||||||
|
'no-global-assign': 'off',
|
||||||
|
// or
|
||||||
|
// 'no-unused-vars': 'warn', // Change the rule to a warning
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+12242
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "agentic-vulnerability-scanner-llm-ui",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve ",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"lucide": "^0.474.0",
|
||||||
|
"vue": "^3.2.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.16",
|
||||||
|
"@babel/eslint-parser": "^7.12.16",
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-vue": "^8.0.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@babel/eslint-parser"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead",
|
||||||
|
"not ie 11"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
|
||||||
|
let URL = window.location.href;
|
||||||
|
if (URL.endsWith('/')) {
|
||||||
|
URL = URL.slice(0, -1);
|
||||||
|
}
|
||||||
|
URL = process.env.VUE_APP_SERVER_URL
|
||||||
|
|
||||||
|
// Vue application
|
||||||
|
let LLM_SPECS = [
|
||||||
|
`POST ${URL}/v1/self-probe
|
||||||
|
Authorization: Bearer XXXXX
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"prompt": "<<PROMPT>>"
|
||||||
|
}
|
||||||
|
|
||||||
|
`,
|
||||||
|
`POST https://api.openai.com/v1/chat/completions
|
||||||
|
Authorization: Bearer $OPENAI_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "gpt-3.5-turbo",
|
||||||
|
"messages": [{"role": "user", "content": "<<PROMPT>>"}],
|
||||||
|
"temperature": 0.7
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
POST https://api.deepseek.com/chat/completions
|
||||||
|
Authorization: Bearer $DEEPSEEK_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "deepseek-chat",
|
||||||
|
"messages": [
|
||||||
|
{"role": "system", "content": "You are a helpful assistant."},
|
||||||
|
{"role": "user", "content": "<<PROMPT>>"}
|
||||||
|
],
|
||||||
|
"stream": false
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST https://api.replicate.com/v1/models/mistralai/mixtral-8x7b-instruct-v0.1/predictions
|
||||||
|
Authorization: Bearer $APIKEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"top_k": 50,
|
||||||
|
"top_p": 0.9,
|
||||||
|
"prompt": "Write a bedtime story about neural networks I can read to my toddler",
|
||||||
|
"temperature": 0.6,
|
||||||
|
"max_new_tokens": 1024,
|
||||||
|
"prompt_template": "<s>[INST] <<PROMPT>> [/INST] ",
|
||||||
|
"presence_penalty": 0,
|
||||||
|
"frequency_penalty": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST https://api.groq.com/v1/request_manager/text_completion
|
||||||
|
Authorization: Bearer XXXXX
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model_id": "codellama-34b",
|
||||||
|
"system_prompt": "You are helpful and concise coding assistant",
|
||||||
|
"user_prompt": "<<PROMPT>>"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST https://api.together.xyz/v1/chat/completions
|
||||||
|
Authorization: Bearer $TOGETHER_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "mistralai/Mixtral-8x7B-Instruct-v0.1",
|
||||||
|
"messages": [
|
||||||
|
{"role": "system", "content": "You are an expert travel guide"},
|
||||||
|
{"role": "user", "content": "<<PROMPT>>"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST ${URL}/v1/self-probe-image
|
||||||
|
Authorization: Bearer XXXXX
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "What is in this image?",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": {
|
||||||
|
"url": f"data:image/jpeg;base64,{<<BASE64_IMAGE>>}"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`,
|
||||||
|
`POST ${URL}/v1/self-probe-file
|
||||||
|
Authorization: Bearer $GROQ_API_KEY
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
|
||||||
|
{
|
||||||
|
"file": "@./sample_audio.m4a",
|
||||||
|
"model": "whisper-large-v3"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST https://api.gemini.com/v1/generate
|
||||||
|
Authorization: Bearer $GEMINI_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "gemini-latest",
|
||||||
|
"prompt": "<<PROMPT>>",
|
||||||
|
"temperature": 0.8,
|
||||||
|
"max_tokens": 150,
|
||||||
|
"top_p": 1.0,
|
||||||
|
"frequency_penalty": 0,
|
||||||
|
"presence_penalty": 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST https://api.anthropic.com/v1/complete
|
||||||
|
Authorization: Bearer $ANTHROPIC_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "claude-v1.3",
|
||||||
|
"prompt": "<<PROMPT>>",
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_tokens_to_sample": 256,
|
||||||
|
"stop_sequences": ["\n\nHuman:"]
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`POST https://api.cohere.ai/generate
|
||||||
|
Authorization: Bearer $COHERE_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"model": "command-xlarge-nightly",
|
||||||
|
"prompt": "<<PROMPT>>",
|
||||||
|
"max_tokens": 300,
|
||||||
|
"temperature": 0.75,
|
||||||
|
"k": 0,
|
||||||
|
"p": 0.75
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
|
||||||
|
`POST https://<<RESOURCE_NAME>>.openai.azure.com/openai/deployments/<<DEPLOYMENT_NAME>>/completions?api-version=2023-06-01-preview
|
||||||
|
Authorization: Bearer $AZURE_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"prompt": "<<PROMPT>>",
|
||||||
|
"max_tokens": 150,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"top_p": 0.9,
|
||||||
|
"frequency_penalty": 0,
|
||||||
|
"presence_penalty": 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
|
||||||
|
`POST https://api.assemblyai.com/v2/transcript
|
||||||
|
Authorization: Bearer $ASSEMBLY_API_KEY
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"audio_url": "<<AUDIO_FILE_URL>>"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
let LLM_CONFIGS = [
|
||||||
|
{ name: 'Custom API', prompts: 40000, customInstructions: 'Requires api spec' },
|
||||||
|
{ name: 'Open AI', prompts: 24000 },
|
||||||
|
{ name: 'Deepseek v1', prompts: 24000 },
|
||||||
|
{ name: 'Replicate', prompts: 40000 },
|
||||||
|
{ name: 'Groq', prompts: 40000 },
|
||||||
|
{ name: 'Together.ai', prompts: 40000 },
|
||||||
|
{ name: 'Custom API Image', prompts: 40000, customInstructions: 'Requires api spec', modality: 'Image' },
|
||||||
|
{ name: 'Custom API Files', prompts: 40000, customInstructions: 'Requires api spec', modality: 'Files' },
|
||||||
|
{ name: 'Gemini', prompts: 40000 },
|
||||||
|
{ name: 'Claude', prompts: 40000 },
|
||||||
|
{ name: 'Cohere', prompts: 40000 },
|
||||||
|
{ name: 'Azure OpenAI', prompts: 40000 },
|
||||||
|
{ name: 'assemblyai', prompts: 40000 },
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
function has_image(spec) {
|
||||||
|
return spec.includes('<<BASE64_IMAGE>>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function has_files(spec) {
|
||||||
|
return spec.includes('multipart/form-data');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _getFailureRateColor(failureRate) {
|
||||||
|
// We're now working with the strength percentage, so no need to invert
|
||||||
|
const strengthRate = 100 - failureRate;
|
||||||
|
|
||||||
|
if (strengthRate >= 95) return 'text-green-400';
|
||||||
|
else if (strengthRate >= 85) return 'text-green-400';
|
||||||
|
else if (strengthRate >= 75) return 'text-green-500';
|
||||||
|
else if (strengthRate >= 65) return 'text-yellow-400';
|
||||||
|
else if (strengthRate >= 55) return 'text-yellow-500';
|
||||||
|
else if (strengthRate >= 45) return 'text-orange-400';
|
||||||
|
else if (strengthRate >= 35) return 'text-orange-500';
|
||||||
|
else if (strengthRate >= 25) return 'text-dark-accent-red';
|
||||||
|
else if (strengthRate >= 15) return 'text-red-400';
|
||||||
|
else if (strengthRate > 0) return 'text-red-500';
|
||||||
|
else return 'text-gray-100'; // This can be the default for strengthRate of 0 or less
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getFailureRateScore(failureRate) {
|
||||||
|
// Convert failureRate to a strength percentage
|
||||||
|
const strengthRate = 100 - failureRate;
|
||||||
|
|
||||||
|
if (strengthRate >= 90) return 'A';
|
||||||
|
else if (strengthRate >= 80) return 'B';
|
||||||
|
else if (strengthRate >= 70) return 'C';
|
||||||
|
else if (strengthRate >= 60) return 'D';
|
||||||
|
else return 'E'; // For strengthRate less than 60
|
||||||
|
}
|
||||||
|
export { LLM_SPECS, LLM_CONFIGS, has_image, has_files, _getFailureRateColor, _getFailureRateScore ,URL };
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 140 B |
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
<header>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>LLM Vulnerability Scanner</title>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
||||||
|
<link href="https://fonts.cdnfonts.com/css/technopollas" rel="stylesheet">
|
||||||
|
<link href="styles/output.css" rel="stylesheet">
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<body class="bg-dark-bg text-dark-text font-sans">
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="vue-app" class="min-h-screen p-8"></div>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
|
||||||
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.scrollbar-hide {
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
!function (t, e) { var o, n, p, r; e.__SV || (window.posthog = e, e._i = [], e.init = function (i, s, a) { function g(t, e) { var o = e.split("."); 2 == o.length && (t = t[o[0]], e = o[1]), t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } } (p = t.createElement("script")).type = "text/javascript", p.async = !0, p.src = s.api_host.replace(".i.posthog.com", "-assets.i.posthog.com") + "/static/array.js", (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(p, r); var u = e; for (void 0 !== a ? u = e[a] = [] : a = "posthog", u.people = u.people || [], u.toString = function (t) { var e = "posthog"; return "posthog" !== a && (e += "." + a), t || (e += " (stub)"), e }, u.people.toString = function () { return u.toString(1) + ".people (stub)" }, o = "init push capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "), n = 0; n < o.length; n++)g(u, o[n]); e._i.push([i, s, a]) }, e.__SV = 1) }(document, window.posthog || []);
|
||||||
|
window.posthog.init('phc_jfYo5xEofW7eJtiU8rLt2Z8jw1E2eW27BxwTJzwRufH', {
|
||||||
|
api_host: 'https://us.i.posthog.com', person_profiles: 'identified_only' // or 'always' to create profiles for anonymous users as well
|
||||||
|
})
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="bg-dark-accent-green text-dark-bg py-4 px-6 rounded-lg mb-28 text-center">
|
||||||
|
<h4 class="text-lg font-semibold">
|
||||||
|
🚀 NEW: Star Agentic Security on
|
||||||
|
<a href="https://github.com/msoedov/agentic_security" target="_blank"
|
||||||
|
class="underline" data-faitracker-click-bind="true">Github</a> 🚀
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Header with Github link -->
|
||||||
|
<header class="flex justify-between items-center mb-8 relative"
|
||||||
|
v-if="false">
|
||||||
|
<div class="w-full absolute left-0 flex justify-center">
|
||||||
|
<h1
|
||||||
|
class="text-2xl font-bold text-gray-400"> <span
|
||||||
|
class="text-2xl font-technopollas text-gray-300">Agentic
|
||||||
|
</span>
|
||||||
|
|
||||||
|
Vulnerability
|
||||||
|
Scanner</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<PageContent/>
|
||||||
|
<PageConfigs/>
|
||||||
|
<PageFooter />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageFooter from "./components/PageFooter.vue";
|
||||||
|
import PageContent from "./components/PageContent.vue";
|
||||||
|
import PageConfigs from "./components/PageConfigs.vue";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PageFooter,
|
||||||
|
PageContent,
|
||||||
|
PageConfigs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Global styles or App.vue specific styles */
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg">
|
||||||
|
<div @click="toggleLLMSpec" class="flex justify-between items-center cursor-pointer">
|
||||||
|
<h2 class="text-2xl font-bold">LLM API Spec</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="showLLMSpec" class="mt-4">
|
||||||
|
<label v-if="isFocused" for="llm-spec" class="block text-sm font-medium mb-2">
|
||||||
|
LLM API Spec, PROMPT variable will be replaced with the testing prompt
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'LLMSpecInput',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showLLMSpec: false,
|
||||||
|
isFocused: false,
|
||||||
|
modelSpec: '',
|
||||||
|
errorMsg: null,
|
||||||
|
okMsg: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleLLMSpec() {
|
||||||
|
this.showLLMSpec = !this.showLLMSpec;
|
||||||
|
},
|
||||||
|
focusTextarea() {
|
||||||
|
this.isFocused = true;
|
||||||
|
},
|
||||||
|
unfocusTextarea() {
|
||||||
|
this.isFocused = false;
|
||||||
|
},
|
||||||
|
adjustHeight(event) {
|
||||||
|
event.target.style.height = 'auto';
|
||||||
|
event.target.style.height = event.target.scrollHeight + 'px';
|
||||||
|
},
|
||||||
|
verifyIntegration() {
|
||||||
|
// Your logic for verifying integration
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
highlightedText() {
|
||||||
|
// Your logic for highlighted text
|
||||||
|
},
|
||||||
|
statusDotClass() {
|
||||||
|
// Your logic for status dot class
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Styles for the LLM Spec Input */
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,907 @@
|
|||||||
|
<template>
|
||||||
|
<main class="max-w-6xl mx-auto space-y-8">
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg">
|
||||||
|
<h2 class="text-2xl font-bold mb-4">Select a Config</h2>
|
||||||
|
|
||||||
|
<div class="flex space-x-4 overflow-x-auto scrollbar-hide">
|
||||||
|
<div
|
||||||
|
v-for="(config, index) in configs"
|
||||||
|
:key="index"
|
||||||
|
@click="selectConfig(index)"
|
||||||
|
class="flex-none w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/5 border-2 rounded-lg p-4 flex flex-col items-start transition-all hover:shadow-md cursor-pointer"
|
||||||
|
:class="{
|
||||||
|
'border-dark-accent-green': selectedConfig === index,
|
||||||
|
'border-gray-600': selectedConfig !== index
|
||||||
|
}">
|
||||||
|
<div class="font-medium mb-2">{{ config.name }}</div>
|
||||||
|
<div class="text-sm text-gray-400">
|
||||||
|
{{ config.customInstructions || 'Requires API key' }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 text-dark-accent-green font-semibold">
|
||||||
|
{{config.modality || 'API'}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Collapsible LLM Spec Input -->
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg" >
|
||||||
|
<div @click="toggleLLMSpec"
|
||||||
|
class="flex justify-between items-center cursor-pointer">
|
||||||
|
|
||||||
|
<h2 class="text-2xl font-bold">LLM API Spec</h2>
|
||||||
|
<span :class="statusDotClass"
|
||||||
|
class="w-3 h-3 rounded-full mr-2"></span>
|
||||||
|
<svg :class="{'rotate-180': showLLMSpec}"
|
||||||
|
class="w-6 h-6 transition-transform duration-200"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="showLLMSpec" class="mt-4">
|
||||||
|
<label v-if="isFocused" for="llm-spec"
|
||||||
|
class="block text-sm font-medium mb-2">
|
||||||
|
LLM API Spec, PROMPT variable will be replaced with the testing
|
||||||
|
prompt
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
v-if="!isFocused"
|
||||||
|
|
||||||
|
class="w-full bg-dark-bg text-dark-accent-orange border border-gray-600 rounded-lg p-3 cursor-text mb-5"
|
||||||
|
@click="focusTextarea"
|
||||||
|
v-html="highlightedText"></div>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
v-else
|
||||||
|
ref="textarea"
|
||||||
|
class="w-full bg-dark-bg text-dark-accent-orange border border-gray-600 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-dark-accent-green"
|
||||||
|
@blur="unfocusTextarea"
|
||||||
|
v-model="modelSpec"
|
||||||
|
@input="adjustHeight"
|
||||||
|
rows="5"
|
||||||
|
placeholder="Enter LLM API Spec here..."></textarea>
|
||||||
|
|
||||||
|
<!-- Error and Success Messages -->
|
||||||
|
<div v-if="errorMsg"
|
||||||
|
class="bg-dark-accent-red bg-opacity-20 border border-dark-accent-red text-dark-accent-red px-4 py-3 rounded-lg relative"
|
||||||
|
role="alert">
|
||||||
|
<strong class="font-bold">Oops!</strong>
|
||||||
|
<span class="block sm:inline">{{errorMsg}}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="okMsg"
|
||||||
|
class="bg-dark-accent-green bg-opacity-20 border border-dark-accent-green text-dark-accent-green px-4 py-3 rounded-lg relative"
|
||||||
|
role="alert">
|
||||||
|
<strong class="font-bold"></strong>
|
||||||
|
<span class="block sm:inline">{{okMsg}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
|
<section class="flex justify-center space-x-4 mt-10">
|
||||||
|
<button
|
||||||
|
@click="verifyIntegration"
|
||||||
|
class="bg-dark-accent-orange text-dark-bg rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
Verify Integration
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- LLM Spec Input -->
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg" v-if="false" >
|
||||||
|
<h2 class="text-2xl font-bold mb-4">LLM API Spec</h2>
|
||||||
|
<label for="llm-spec" class="block text-sm font-medium mb-2">
|
||||||
|
LLM API Spec, PROMPT variable will be replaced with the testing
|
||||||
|
prompt
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
class="w-full bg-dark-bg text-dark-accent-orange border border-gray-600 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-dark-accent-green"
|
||||||
|
id="llm-spec"
|
||||||
|
ref="textarea"
|
||||||
|
v-model="modelSpec"
|
||||||
|
@input="adjustHeight"
|
||||||
|
rows="5"
|
||||||
|
placeholder="Enter LLM API Spec here..."></textarea>
|
||||||
|
</section>
|
||||||
|
<section
|
||||||
|
class="bg-dark-card rounded-lg p-6 shadow-lg mt-8 border-dark-accent-green border-2">
|
||||||
|
<div @click="toggleParams"
|
||||||
|
class="flex justify-between items-center cursor-pointer">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2"
|
||||||
|
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
|
||||||
|
</svg>
|
||||||
|
<h2 class="text-2xl font-bold">Parameters</h2>
|
||||||
|
</div>
|
||||||
|
<svg :class="{'rotate-180': showParams}"
|
||||||
|
class="w-6 h-6 transition-transform duration-200"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div v-show="showParams" class="mt-4">
|
||||||
|
<div class="flex items-center justify-end mt-4">
|
||||||
|
<button
|
||||||
|
@click="confirmResetState"
|
||||||
|
class="flex items-center bg-dark-accent-red text-dark-bg rounded-lg px-4 py-2 text-sm font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2"
|
||||||
|
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
|
Reset State
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Confirmation Modal -->
|
||||||
|
<div
|
||||||
|
v-if="showResetConfirmation"
|
||||||
|
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
|
<div class="bg-dark-card rounded-lg p-6 max-w-sm w-full">
|
||||||
|
<h3 class="text-xl font-bold mb-4 text-dark-text">Confirm
|
||||||
|
Reset</h3>
|
||||||
|
<p class="text-gray-400 mb-6">Are you sure you want to reset all
|
||||||
|
settings to their default state? This action cannot be
|
||||||
|
undone.</p>
|
||||||
|
<div class="flex justify-end space-x-4">
|
||||||
|
<button
|
||||||
|
@click="showResetConfirmation = false"
|
||||||
|
class="bg-gray-600 text-dark-text rounded-lg px-4 py-2 hover:bg-opacity-80 transition-colors">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="resetState"
|
||||||
|
class="bg-dark-accent-red text-dark-bg rounded-lg px-4 py-2 hover:bg-opacity-80 transition-colors">
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Confirmation Modal -->
|
||||||
|
|
||||||
|
<!-- Maximum Budget Slider -->
|
||||||
|
<!-- Budget Slider -->
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg">
|
||||||
|
<h2 class="text-2xl font-bold mb-4">Maximum Budget</h2>
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<span class="text-lg">1M Tokens</span>
|
||||||
|
<input
|
||||||
|
v-model="budget"
|
||||||
|
@change="updateBudgetFromInput"
|
||||||
|
class="w-20 bg-dark-bg text-dark-text border border-gray-600 rounded-lg p-2 text-center"
|
||||||
|
type="text" />
|
||||||
|
<span class="text-lg">100M Tokens</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-model="budget"
|
||||||
|
@input="updateBudgetFromSlider"
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
step="1"
|
||||||
|
class="w-full h-2 bg-gray-600 rounded-lg appearance-none cursor-pointer">
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Optimize Toggle -->
|
||||||
|
<div class="flex flex-col mt-6 mr-10 ml-10">
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<h3 class="text-lg font-semibold">Optimize Test</h3>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input type="checkbox" v-model="optimize"
|
||||||
|
class="sr-only peer">
|
||||||
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-dark-accent-green rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-dark-accent-green"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-400 mt-2 mb-6">
|
||||||
|
When enabled, this option runs a Bayesian optimization loop to
|
||||||
|
find the most effective test parameters. This can potentially
|
||||||
|
reduce the cost and the total running time of your vulnerability
|
||||||
|
scan, but may reduce accuracy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Chart Diagram Toggle -->
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<h3 class="text-lg font-semibold">Enable Chart Diagram</h3>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input type="checkbox" v-model="enableChartDiagram"
|
||||||
|
class="sr-only peer">
|
||||||
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-dark-accent-green rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-dark-accent-green"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-400 mt-2 mb-6">
|
||||||
|
When enabled, a chart diagram will be generated to visualize the
|
||||||
|
results of your vulnerability scan.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Logging Toggle -->
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<h3 class="text-lg font-semibold">Enable Detailed Logging</h3>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input type="checkbox" v-model="enableLogging"
|
||||||
|
class="sr-only peer">
|
||||||
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-dark-accent-green rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-dark-accent-green"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-400 mt-2 mb-6">
|
||||||
|
When enabled, detailed logs will be generated during the
|
||||||
|
vulnerability scan process. This can be useful for debugging and
|
||||||
|
in-depth analysis.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Concurrency Toggle -->
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<h3 class="text-lg font-semibold">Enable Concurrency</h3>
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input type="checkbox" v-model="enableConcurrency"
|
||||||
|
class="sr-only peer">
|
||||||
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-dark-accent-green rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-dark-accent-green"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-400 mt-2">
|
||||||
|
When enabled, the vulnerability scan will run multiple tests
|
||||||
|
concurrently. This can significantly reduce the total scan time
|
||||||
|
but may increase resource usage.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Modules Selection -->
|
||||||
|
<section
|
||||||
|
class="bg-dark-card rounded-lg p-6 shadow-lg border-dark-accent-red border-4">
|
||||||
|
<div @click="toggleModules"
|
||||||
|
class="flex justify-between items-center cursor-pointer">
|
||||||
|
<h2 class="text-2xl font-bold">Modules [{{selectedDS}}
|
||||||
|
selected]</h2>
|
||||||
|
<svg :class="{'rotate-180': showModules}"
|
||||||
|
class="w-6 h-6 transition-transform duration-200"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="showModules" class="mt-4">
|
||||||
|
<!-- Many-shot jailbreaking Toggle -->
|
||||||
|
<div v-if="enableMultiStepAttack" class="alert-box mt-4">
|
||||||
|
<div
|
||||||
|
class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded relative"
|
||||||
|
role="alert">
|
||||||
|
<strong class="font-bold">Notice:</strong>
|
||||||
|
<span class="block sm:inline">A many-shot attack might take a
|
||||||
|
longer time to complete.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between mb-2 mt-10">
|
||||||
|
<h3 class="text-lg font-semibold">Enable Many-shot
|
||||||
|
jailbreaking</h3>
|
||||||
|
|
||||||
|
<label class="relative inline-flex items-center cursor-pointer">
|
||||||
|
<input type="checkbox" v-model="enableMultiStepAttack"
|
||||||
|
class="sr-only peer">
|
||||||
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-dark-accent-green rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-dark-accent-green"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-400 mt-2 mb-2">
|
||||||
|
When enabled, the scan will attempt Many-shot jailbreaking
|
||||||
|
simulations
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div v-if="hasFileSpec" class="alert-box mt-10">
|
||||||
|
<div
|
||||||
|
class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded relative"
|
||||||
|
role="alert">
|
||||||
|
<strong class="font-bold">Notice:</strong>
|
||||||
|
<span class="block sm:inline">Converting audio or image prompts
|
||||||
|
might
|
||||||
|
take some time to compute.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between mb-4 mt-4">
|
||||||
|
<button @click="selectAllPackages"
|
||||||
|
class="text-dark-accent-green hover:underline">Select
|
||||||
|
All</button>
|
||||||
|
<button @click="deselectAllPackages"
|
||||||
|
class="text-gray-400 hover:underline">Deselect All</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
|
<div
|
||||||
|
v-for="(pkg, index) in dataConfig"
|
||||||
|
:key="index"
|
||||||
|
@click="addPackage(index)"
|
||||||
|
class="border rounded-lg p-3 cursor-pointer transition-all hover:shadow-md overflow-hidden"
|
||||||
|
:class="{
|
||||||
|
'border-dark-accent-green bg-dark-accent-green bg-opacity-20': pkg.selected,
|
||||||
|
'border-gray-600': !pkg.selected
|
||||||
|
}">
|
||||||
|
<div class="font-medium mb-1 truncate">{{ pkg.dataset_name
|
||||||
|
}}</div>
|
||||||
|
<div class="text-sm text-gray-400 truncate">
|
||||||
|
{{ pkg.source || 'Local dataset' }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 text-sm font-semibold">
|
||||||
|
{{ pkg.dynamic ? 'Dynamic dataset' :
|
||||||
|
`${pkg.num_prompts.toLocaleString()} prompts` }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Error and Success Messages -->
|
||||||
|
<div v-if="errorMsg"
|
||||||
|
class="bg-dark-accent-red bg-opacity-20 border border-dark-accent-red text-dark-accent-red px-4 py-3 rounded-lg relative"
|
||||||
|
role="alert">
|
||||||
|
<strong class="font-bold">Oops!</strong>
|
||||||
|
<span class="block sm:inline">{{errorMsg}}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="okMsg"
|
||||||
|
class="bg-dark-accent-green bg-opacity-20 border border-dark-accent-green text-dark-accent-green px-4 py-3 rounded-lg relative"
|
||||||
|
role="alert">
|
||||||
|
<strong class="font-bold">></strong>
|
||||||
|
<span class="block sm:inline">{{okMsg}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
|
<section class="flex justify-center space-x-4">
|
||||||
|
<button
|
||||||
|
@click="verifyIntegration"
|
||||||
|
class="bg-dark-accent-orange text-dark-bg rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
Verify Integration
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="startScan"
|
||||||
|
v-if="!scanRunning"
|
||||||
|
class="bg-dark-accent-green text-dark-bg rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
||||||
|
viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="mr-2"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
|
||||||
|
Run Scan
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="stopScan"
|
||||||
|
v-if="scanRunning"
|
||||||
|
class="bg-dark-accent-red text-dark-bg rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors flex items-center">
|
||||||
|
<!-- Stop Icon -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
||||||
|
viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="mr-2"><rect x="6" y="6" width="12"
|
||||||
|
height="12"></rect></svg>
|
||||||
|
Stop Scan
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Progress Bar -->
|
||||||
|
<div id="progress"
|
||||||
|
class="bg-dark-accent-green rounded-full h-2 transition-all duration-500 ease-in-out"
|
||||||
|
v-bind:style="{width: progressWidth}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scan Results -->
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg"
|
||||||
|
v-if="mainTable.length > 0">
|
||||||
|
<h2 class="text-2xl font-bold mb-4">Scan Results</h2>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-left">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-gray-600">
|
||||||
|
<th class="p-3">Vulnerability Module</th>
|
||||||
|
<th class="p-3">% Strength</th>
|
||||||
|
<th class="p-3">Number of Tokens</th>
|
||||||
|
<th class="p-3">Cost (in gpt-3 tokens)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="result in mainTable" :key="result.module || index" class="border-b border-gray-700"
|
||||||
|
:class="{'text-dark-accent-green': result.last, 'text-gray-300': !result.last}">
|
||||||
|
<td class="p-3">{{result.module}}</td>
|
||||||
|
<td class="p-3 text-gray-100"
|
||||||
|
:class="getFailureRateColor(result.failureRate)">
|
||||||
|
{{getFailureRateScore(result.failureRate)}}( {{(100 -
|
||||||
|
result.failureRate).toFixed(2)}} )
|
||||||
|
</td>
|
||||||
|
<td class="p-3">{{result.tokens}}k</td>
|
||||||
|
<td class="p-3">${{result.cost.toFixed(2)}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Download Button -->
|
||||||
|
<button
|
||||||
|
@click="downloadFailures"
|
||||||
|
class="bg-dark-accent-yellow text-dark-bg rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
Download failures
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Report Image -->
|
||||||
|
<img :src="reportImageUrl" alt="Generated Plot" v-if="reportImageUrl"
|
||||||
|
loading="lazy" class="mx-auto rounded-lg shadow-lg">
|
||||||
|
|
||||||
|
<!-- Logs Section -->
|
||||||
|
<section class="bg-dark-card rounded-lg p-6 shadow-lg mt-8">
|
||||||
|
<div @click="toggleLogs"
|
||||||
|
class="flex justify-between items-center cursor-pointer">
|
||||||
|
<h2 class="text-2xl font-bold">Logs</h2>
|
||||||
|
<svg :class="{'rotate-180': showLogs}"
|
||||||
|
class="w-6 h-6 transition-transform duration-200"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="showLogs" class="mt-4">
|
||||||
|
<div class="mb-4 flex justify-between items-center">
|
||||||
|
<span class="text-sm text-gray-400">Showing latest {{
|
||||||
|
Math.min(logs.length, maxDisplayedLogs) }} of {{ logs.length }}
|
||||||
|
logs</span>
|
||||||
|
<button @click="downloadLogs"
|
||||||
|
class="bg-dark-accent-green text-dark-bg rounded-lg px-4 py-2 text-sm font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
Download Logs
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="bg-dark-bg p-4 rounded-lg max-h-96 overflow-y-auto">
|
||||||
|
<div v-for="(log, index) in displayedLogs" :key="index"
|
||||||
|
class="mb-2 last:mb-0">
|
||||||
|
<span class="text-dark-accent-green">{{ log.timestamp }}</span>
|
||||||
|
<span class="ml-2"
|
||||||
|
:class="{'text-dark-accent-red': log.level === 'ERROR'}">{{
|
||||||
|
log.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { LLM_CONFIGS, LLM_SPECS,has_image, has_files, _getFailureRateColor, _getFailureRateScore,URL } from '../../public/base.js';
|
||||||
|
import { ref, useTemplateRef, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const textarea= useTemplateRef('textarea')
|
||||||
|
export default{
|
||||||
|
name: 'PageConfigs',
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
progressWidth: '0%',
|
||||||
|
modelSpec: LLM_SPECS[0],
|
||||||
|
budget: 50,
|
||||||
|
isFocused: false, // Tracks if the textarea is focused
|
||||||
|
showParams: false,
|
||||||
|
showResetConfirmation: false,
|
||||||
|
enableChartDiagram: true,
|
||||||
|
enableLogging: false,
|
||||||
|
enableConcurrency: false,
|
||||||
|
optimize: false,
|
||||||
|
enableMultiStepAttack: false,
|
||||||
|
scanResults: [],
|
||||||
|
mainTable: [],
|
||||||
|
integrationVerified: false,
|
||||||
|
scanRunning: false,
|
||||||
|
errorMsg: '',
|
||||||
|
maskMode: false,
|
||||||
|
okMsg: '',
|
||||||
|
reportImageUrl: '',
|
||||||
|
selectedConfig: 0,
|
||||||
|
showModules: false,
|
||||||
|
showLogs: false,
|
||||||
|
showConsentModal: true,
|
||||||
|
statusDotClass: 'bg-gray-500', // Default status dot class
|
||||||
|
statusText: 'Verified', // Default status text
|
||||||
|
statusClass: 'bg-green-500 text-dark-bg', // Default status class
|
||||||
|
showLLMSpec: true, // Default to showing the LLM Spec Input
|
||||||
|
logs: [], // This will store all the logs
|
||||||
|
maxDisplayedLogs: 50, // Maximum number of logs to display
|
||||||
|
configs: LLM_CONFIGS,
|
||||||
|
dataConfig: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// Check if consent is already given in local storage
|
||||||
|
const consentGiven = localStorage.getItem('consentGiven');
|
||||||
|
if (consentGiven === 'true') {
|
||||||
|
this.showConsentModal = false; // Don't show the modal if consent was given
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.adjustHeight({ target: this.$refs.textarea });
|
||||||
|
// this.startScan();
|
||||||
|
this.loadConfigs();
|
||||||
|
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedDS: function () {
|
||||||
|
return this.dataConfig.filter(p => p.selected).length;
|
||||||
|
},
|
||||||
|
displayedLogs() {
|
||||||
|
return this.logs.slice(-this.maxDisplayedLogs).reverse();
|
||||||
|
},
|
||||||
|
hasImageSpec() {
|
||||||
|
return has_image(this.modelSpec);
|
||||||
|
},
|
||||||
|
hasAudioSpec() {
|
||||||
|
return has_files(this.modelSpec);
|
||||||
|
},
|
||||||
|
hasFileSpec() {
|
||||||
|
return has_files(this.modelSpec) || has_image(this.modelSpec);
|
||||||
|
},
|
||||||
|
highlightedText() {
|
||||||
|
// First highlight <<VAR>> pattern
|
||||||
|
let text = this.modelSpec.replace(
|
||||||
|
/<<([^>]+)>>/g,
|
||||||
|
`<span class="px-2 py-0.5 rounded-full bg-dark-accent-yellow text-dark-bg font-medium"><<$1>></span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Then highlight $VARIABLE pattern
|
||||||
|
text = text.replace(
|
||||||
|
/(\$[A-Z_]+)/g,
|
||||||
|
`<span class="px-2 py-0.5 rounded-full bg-yellow-100 text-dark-bg font-medium">$1</span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Finally wrap everything in gray text
|
||||||
|
return `<span class="text-gray-500">${text}</span>`;
|
||||||
|
},
|
||||||
|
highlightedText2() {
|
||||||
|
// First apply the highlighting for variables
|
||||||
|
const highlightedText = this.modelSpec.replace(
|
||||||
|
/<<([^>]+)>>/g,
|
||||||
|
`<span class="px-2 py-0.5 rounded-full bg-dark-accent-yellow text-dark-bg font-medium"><<$1>></span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wrap the entire text in a span to make non-highlighted parts dim gray
|
||||||
|
return `<span class="text-gray-500">${highlightedText}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focusTextarea() {
|
||||||
|
this.isFocused = true;
|
||||||
|
self = this.$refs;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// Focus the textarea after rendering
|
||||||
|
this.$refs.textarea?.focus();
|
||||||
|
this.adjustHeight({ target: this.$refs.textarea });
|
||||||
|
});
|
||||||
|
document.addEventListener("mousedown", this.handleClickOutside);
|
||||||
|
|
||||||
|
},
|
||||||
|
handleOutsideClick(event) {
|
||||||
|
if (!this.$refs.container.contains(event.target)) {
|
||||||
|
this.isFocused = false;
|
||||||
|
document.removeEventListener("mousedown", this.handleClickOutside);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unfocusTextarea() {
|
||||||
|
this.isFocused = false;
|
||||||
|
},
|
||||||
|
acceptConsent() {
|
||||||
|
this.showConsentModal = false; // Close the modal
|
||||||
|
localStorage.setItem('consentGiven', 'true'); // Save consent to local storage
|
||||||
|
},
|
||||||
|
|
||||||
|
saveStateToLocalStorage() {
|
||||||
|
const state = {
|
||||||
|
modelSpec: this.modelSpec,
|
||||||
|
budget: this.budget,
|
||||||
|
dataConfig: this.dataConfig,
|
||||||
|
optimize: this.optimize,
|
||||||
|
enableChartDiagram: this.enableChartDiagram,
|
||||||
|
enableMultiStepAttack: this.enableMultiStepAttack,
|
||||||
|
};
|
||||||
|
localStorage.setItem('appState:v1', JSON.stringify(state));
|
||||||
|
},
|
||||||
|
loadStateFromLocalStorage() {
|
||||||
|
const savedState = localStorage.getItem('appState:v1');
|
||||||
|
console.log('Loading state from local storage:', savedState);
|
||||||
|
if (savedState) {
|
||||||
|
const state = JSON.parse(savedState);
|
||||||
|
this.modelSpec = state.modelSpec;
|
||||||
|
this.budget = state.budget;
|
||||||
|
this.dataConfig = state.dataConfig;
|
||||||
|
this.optimize = state.optimize;
|
||||||
|
this.enableChartDiagram = state.enableChartDiagram;
|
||||||
|
this.enableMultiStepAttack = state.enableMultiStepAttack;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetState() {
|
||||||
|
localStorage.removeItem('appState:v1');
|
||||||
|
this.modelSpec = LLM_SPECS[0];
|
||||||
|
this.budget = 50;
|
||||||
|
this.dataConfig.forEach(config => config.selected = false);
|
||||||
|
this.optimize = false;
|
||||||
|
this.enableChartDiagram = true;
|
||||||
|
this.okMsg = '';
|
||||||
|
this.errorMsg = '';
|
||||||
|
this.integrationVerified = false;
|
||||||
|
this.showResetConfirmation = false;
|
||||||
|
this.enableMultiStepAttack = false;
|
||||||
|
},
|
||||||
|
confirmResetState() {
|
||||||
|
this.showResetConfirmation = true;
|
||||||
|
},
|
||||||
|
updateStatusDot(ok) {
|
||||||
|
if (ok) {
|
||||||
|
this.statusDotClass = 'bg-green-500'; // Green when expanded
|
||||||
|
} else if (!ok) {
|
||||||
|
this.statusDotClass = 'bg-orange-500'; // Orange if collapsed with content
|
||||||
|
} else {
|
||||||
|
this.statusDotClass = 'bg-gray-500'; // Gray if collapsed without content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleLLMSpec() {
|
||||||
|
this.showLLMSpec = !this.showLLMSpec;
|
||||||
|
},
|
||||||
|
// adjustHeight(event) {
|
||||||
|
// console.log(event,"event")
|
||||||
|
// const textarea = event.target;
|
||||||
|
// event.target.style.height = 'auto';
|
||||||
|
// event.target.style.height = event.target.scrollHeight + 'px';
|
||||||
|
// },
|
||||||
|
downloadFailures() {
|
||||||
|
window.open('/failures', '_blank');
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
this.maskMode = !this.maskMode;
|
||||||
|
},
|
||||||
|
verifyIntegration: async function () {
|
||||||
|
let payload = {
|
||||||
|
spec: this.modelSpec,
|
||||||
|
};
|
||||||
|
const response = await fetch(`${URL}/verify`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
console.log(response);
|
||||||
|
let txt = await response.text();
|
||||||
|
if (!response.ok) {
|
||||||
|
this.updateStatusDot(false);
|
||||||
|
this.errorMsg = 'Integration verification failed:' + txt;
|
||||||
|
} else {
|
||||||
|
this.errorMsg = '';
|
||||||
|
this.updateStatusDot(true);
|
||||||
|
this.okMsg = 'Integration verified';
|
||||||
|
this.integrationVerified = true;
|
||||||
|
// console.log('Integration verified', this.integrationVerified);
|
||||||
|
// this.$forceUpdate();
|
||||||
|
|
||||||
|
}
|
||||||
|
this.saveStateToLocalStorage();
|
||||||
|
},
|
||||||
|
loadConfigs: async function () {
|
||||||
|
const response = await fetch(`${URL}/v1/data-config`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log(response);
|
||||||
|
this.dataConfig = await response?.json();
|
||||||
|
this.loadStateFromLocalStorage();
|
||||||
|
},
|
||||||
|
selectConfig(index) {
|
||||||
|
this.selectedConfig = index;
|
||||||
|
this.modelSpec = LLM_SPECS[index];
|
||||||
|
this.adjustHeight({ target: this.$refs.textarea });
|
||||||
|
// this.adjustHeight({ target: document.getElementById('llm-spec') });
|
||||||
|
this.errorMsg = '';
|
||||||
|
this.okMsg = '';
|
||||||
|
this.integrationVerified = false;
|
||||||
|
},
|
||||||
|
toggleModules() {
|
||||||
|
this.showModules = !this.showModules;
|
||||||
|
},
|
||||||
|
toggleLogs() {
|
||||||
|
this.showLogs = !this.showLogs;
|
||||||
|
},
|
||||||
|
addLog(message, level = 'INFO') {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
this.logs.push({ timestamp, message, level });
|
||||||
|
},
|
||||||
|
downloadLogs() {
|
||||||
|
const logText = this.logs.map(log => `${log.timestamp} [${log.level}] ${log.message}`).join('\n');
|
||||||
|
const blob = new Blob([logText], { type: 'text/plain' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'vulnerability_scan_logs.txt';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
},
|
||||||
|
addPackage(index) {
|
||||||
|
|
||||||
|
const pkg = this.dataConfig[index];
|
||||||
|
pkg.selected = !pkg.selected;
|
||||||
|
|
||||||
|
},
|
||||||
|
getFailureRateScore(failureRate) {
|
||||||
|
return _getFailureRateScore(failureRate);
|
||||||
|
},
|
||||||
|
getFailureRateColor(failureRate) {
|
||||||
|
return _getFailureRateColor(failureRate);
|
||||||
|
},
|
||||||
|
toggleParams() {
|
||||||
|
this.showParams = !this.showParams;
|
||||||
|
},
|
||||||
|
adjustHeight(event) {
|
||||||
|
const element = event.target;
|
||||||
|
if (!element) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Reset height to ensure accurate measurement
|
||||||
|
element.style.height = 'auto';
|
||||||
|
// Adjust height based on scrollHeight
|
||||||
|
element.style.height = `${element.scrollHeight + 100}px`;
|
||||||
|
},
|
||||||
|
newEvent: function (event) {
|
||||||
|
|
||||||
|
if (event.status) {
|
||||||
|
this.okMsg = `${event.module}`;
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('New event');
|
||||||
|
// { "module": "Module 49", "tokens": 480, "cost": 4.800000000000001, "progress": 9.8 }
|
||||||
|
let progress = event.progress;
|
||||||
|
progress = progress % 100;
|
||||||
|
this.progressWidth = `${progress}%`;
|
||||||
|
this.addLog(`${JSON.stringify(event)}`, 'INFO');
|
||||||
|
if (this.mainTable.length < 1) {
|
||||||
|
this.mainTable.push(event);
|
||||||
|
event.last = true;
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let last = this.mainTable[this.mainTable.length - 1];
|
||||||
|
if (last.module === event.module) {
|
||||||
|
last.tokens = event.tokens;
|
||||||
|
last.cost = event.cost;
|
||||||
|
last.progress = event.progress;
|
||||||
|
last.failureRate = event.failureRate;
|
||||||
|
} else {
|
||||||
|
last.last = false;
|
||||||
|
this.mainTable.push(event);
|
||||||
|
event.last = true;
|
||||||
|
this.newRow()
|
||||||
|
}
|
||||||
|
this.okMsg = `New event: ${event.module}: ${event.progress}%`;
|
||||||
|
|
||||||
|
},
|
||||||
|
newRow: async function () {
|
||||||
|
if (!this.enableChartDiagram) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('New row');
|
||||||
|
let payload = {
|
||||||
|
table: this.mainTable,
|
||||||
|
};
|
||||||
|
const response = await fetch(`${URL}/plot.jpeg`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
// Convert image response to a data URL for the <img> src
|
||||||
|
const blob = await response.blob();
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
reader.onloadend = () => {
|
||||||
|
this.reportImageUrl = reader.result;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
selectAllPackages() {
|
||||||
|
const allSelected = this.dataConfig.every(pkg => pkg.selected);
|
||||||
|
|
||||||
|
// If all are selected, deselect all. Otherwise, select all.
|
||||||
|
this.dataConfig.forEach(pkg => {
|
||||||
|
pkg.selected = !allSelected;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updateSelectedDS();
|
||||||
|
},
|
||||||
|
|
||||||
|
deselectAllPackages() {
|
||||||
|
this.dataConfig.forEach(pkg => {
|
||||||
|
pkg.selected = false;
|
||||||
|
});
|
||||||
|
this.updateSelectedDS();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSelectedDS() {
|
||||||
|
this.selectedDS = this.dataConfig.filter(pkg => pkg.selected).length;
|
||||||
|
},
|
||||||
|
updateBudgetFromSlider(event) {
|
||||||
|
this.budget = parseInt(event.target.value);
|
||||||
|
},
|
||||||
|
updateBudgetFromInput(event) {
|
||||||
|
let value = parseInt(event.target.value);
|
||||||
|
if (isNaN(value) || value < 1) {
|
||||||
|
value = 1;
|
||||||
|
} else if (value > 100) {
|
||||||
|
value = 100;
|
||||||
|
}
|
||||||
|
this.budget = value;
|
||||||
|
},
|
||||||
|
stopScan: async function () {
|
||||||
|
this.scanRunning = false;
|
||||||
|
const response = await fetch(`${URL}/stop`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
startScan: async function () {
|
||||||
|
this.showLLMSpec = false;
|
||||||
|
let payload = {
|
||||||
|
maxBudget: this.budget,
|
||||||
|
llmSpec: this.modelSpec,
|
||||||
|
datasets: this.dataConfig,
|
||||||
|
optimize: this.optimize,
|
||||||
|
enableMultiStepAttack: this.enableMultiStepAttack,
|
||||||
|
};
|
||||||
|
const response = await fetch(`${URL}/scan`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
this.okMsg = 'Scan started';
|
||||||
|
this.mainTable = [];
|
||||||
|
this.scanRunning = true;
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
let receivedLength = 0; // received that many bytes at the moment
|
||||||
|
let chunks = []; // array of received binary chunks (comprises the body)
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks.push(value);
|
||||||
|
receivedLength += value.length;
|
||||||
|
|
||||||
|
const chunkAsString = new TextDecoder("utf-8").decode(value);
|
||||||
|
const chunkAsLines = chunkAsString.split('\n').filter(line => line.trim());
|
||||||
|
|
||||||
|
self = this;
|
||||||
|
chunkAsLines.forEach(line => {
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(line);
|
||||||
|
self.scanResults.push(result);
|
||||||
|
self.newEvent(result);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing chunk:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.saveStateToLocalStorage();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<div id="consent-modal" v-if="showConsentModal"
|
||||||
|
class="fixed inset-0 bg-black bg-opacity-75 flex justify-center items-center z-50">
|
||||||
|
<div
|
||||||
|
class="bg-dark-card text-dark-text p-8 rounded-xl shadow-2xl max-w-xl w-full">
|
||||||
|
<h2 class="text-2xl font-bold mb-6 text-center">AI Red Team Ethical
|
||||||
|
Use Agreement</h2>
|
||||||
|
<div class="space-y-6">
|
||||||
|
<p class="text-sm leading-relaxed">
|
||||||
|
This AI red team tool is designed for security research,
|
||||||
|
vulnerability assessment,
|
||||||
|
and responsible testing purposes. By accessing this tool, you
|
||||||
|
explicitly agree to
|
||||||
|
the following ethical guidelines:
|
||||||
|
</p>
|
||||||
|
<ul class="list-disc list-inside text-sm space-y-3">
|
||||||
|
<li>
|
||||||
|
<strong>Consent and Authorization:</strong> You will only
|
||||||
|
use
|
||||||
|
this tool on systems
|
||||||
|
for which you have explicit, documented permission from the
|
||||||
|
system owners.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Responsible Disclosure:</strong> Any vulnerabilities
|
||||||
|
discovered must be
|
||||||
|
reported responsibly to the appropriate parties,
|
||||||
|
prioritizing
|
||||||
|
system and user safety.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>No Malicious Intent:</strong> You will not use this
|
||||||
|
tool
|
||||||
|
to cause harm,
|
||||||
|
disrupt services, or compromise the integrity of any system
|
||||||
|
or
|
||||||
|
data.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Legal Compliance:</strong> All testing and research
|
||||||
|
must
|
||||||
|
comply with
|
||||||
|
applicable local, national, and international laws and
|
||||||
|
regulations.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="text-xs text-gray-400 italic">
|
||||||
|
Violation of these terms may result in immediate termination of
|
||||||
|
access and
|
||||||
|
potential legal consequences.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center space-x-4 mt-8">
|
||||||
|
<button
|
||||||
|
@click="declineConsent"
|
||||||
|
class="bg-dark-accent-red text-white rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
Decline
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="acceptConsent"
|
||||||
|
class="bg-dark-accent-green text-dark-bg rounded-lg px-6 py-3 font-medium hover:bg-opacity-80 transition-colors">
|
||||||
|
I Agree and Understand
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageContent',
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showConsentModal: true // Default to true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['accept', 'decline'], // Define the custom events
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
acceptConsent() {
|
||||||
|
this.showConsentModal = false; // Close the modal
|
||||||
|
localStorage.setItem('consentGiven', 'true'); // Save consent to local storage
|
||||||
|
},
|
||||||
|
declineConsent() {
|
||||||
|
this.showConsentModal = false; // Close the modal
|
||||||
|
localStorage.setItem('consentGiven', 'false'); // Save decline to local storage
|
||||||
|
window.location.href = 'https://www.google.com'; // Redirect to Google
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style >
|
||||||
|
/* Styles for the consent modal */
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<footer class="mt-16 pt-8 border-t border-gray-800">
|
||||||
|
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-dark-accent-green mb-4">Home</h3>
|
||||||
|
<p class="text-gray-400">Dedicated to LLM Security, 2024</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-dark-accent-green mb-4">Connect</h3>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://x.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-gray-400 hover:text-dark-accent-green"
|
||||||
|
>X.com</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/msoedov"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-gray-400 hover:text-dark-accent-green"
|
||||||
|
>Github</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-dark-accent-green mb-4">About</h3>
|
||||||
|
<p class="text-gray-400">
|
||||||
|
This is the LLM Vulnerability Scanner. Easy to use—no coding
|
||||||
|
needed, just pure security testing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 pt-8 border-t border-gray-800 text-center">
|
||||||
|
<p class="text-gray-400">Made with ❤️ by the Agentic Security Team</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageFooter', // Descriptive name
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Footer-specific styles here */
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div>hello</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageHeader', // Give a descriptive name
|
||||||
|
// No specific JavaScript logic needed for this simple header
|
||||||
|
// You can add props if you want to make the title dynamic:
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: 'LLM Vulnerability Scanner' // Default title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Any header-specific styles can go here */
|
||||||
|
/* If you are using tailwind, you can include this as well*/
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue' // Create App.vue (see next step)
|
||||||
|
import '../public/base.js' // If you have this file, move it to src/assets
|
||||||
|
import '../public/telemetry.js' // Move to src/assets
|
||||||
|
import lucide from 'lucide' // Import lucide if you are using it
|
||||||
|
const app = createApp(App)
|
||||||
|
app.mount('#vue-app') // Change #vue-app to #app
|
||||||
|
|
||||||
|
app.config.globalProperties.$lucide = lucide
|
||||||
|
|
||||||
|
//lucide.createIcons(); // Create icons
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||||
|
darkMode: 'class',
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Inter', 'sans-serif'],
|
||||||
|
technopollas: ['Technopollas', 'sans-serif'],
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
dark: {
|
||||||
|
bg: '#121212',
|
||||||
|
card: '#1E1E1E',
|
||||||
|
text: '#FFFFFF',
|
||||||
|
accent: {
|
||||||
|
green: '#4CAF50',
|
||||||
|
red: '#F44336',
|
||||||
|
orange: '#FF9800',
|
||||||
|
yellow: '#FFEB3B',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
'lg': '1rem',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
module.exports = defineConfig({ transpileDependencies: true, publicPath: '/' ,devServer: { allowedHosts: 'all', client: {webSocketURL: 'auto://0.0.0.0:0/ws'}}, })
|
||||||
Reference in New Issue
Block a user