Skip to content

Commit e01f969

Browse files
committed
feat(039): Configure button on all installed scanners + custom env vars
Configure button shows for all installed/configured scanners. Dialog supports required, optional, and custom env vars with keyring integration. Users can add OPENAI_API_KEY etc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 44feed9 commit e01f969

23 files changed

Lines changed: 234 additions & 12 deletions

frontend/src/views/Security.vue

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
Install
100100
</button>
101101
<button
102-
v-if="scanner.status === 'installed' && scanner.required_env?.length"
102+
v-if="scanner.status === 'installed' || scanner.status === 'configured'"
103103
@click="openConfigDialog(scanner)"
104104
class="btn btn-sm btn-outline"
105105
>
@@ -215,30 +215,52 @@
215215

216216
<!-- Configure Scanner Dialog -->
217217
<dialog ref="configDialog" class="modal">
218-
<div class="modal-box">
218+
<div class="modal-box max-w-lg">
219219
<h3 class="font-bold text-lg">Configure {{ configScanner?.name }}</h3>
220+
<p class="text-sm text-base-content/60 mt-1">Set API keys and environment variables. Secrets are stored in your OS keychain.</p>
220221
<div class="py-4 space-y-4" v-if="configScanner">
221-
<div v-for="env in configScanner.required_env" :key="env.key" class="form-control">
222-
<label class="label"><span class="label-text">{{ env.label }}</span></label>
222+
<!-- Required env vars -->
223+
<div v-for="env in (configScanner.required_env || [])" :key="env.key" class="form-control">
224+
<label class="label">
225+
<span class="label-text font-medium">{{ env.label }}</span>
226+
<span class="badge badge-sm badge-error">Required</span>
227+
</label>
223228
<input
224229
v-model="configValues[env.key]"
225230
:type="env.secret ? 'password' : 'text'"
226-
:placeholder="env.key"
231+
:placeholder="configuredPlaceholder(env.key)"
227232
class="input input-bordered"
228233
/>
229234
</div>
235+
<!-- Optional env vars -->
230236
<div v-for="env in (configScanner.optional_env || [])" :key="env.key" class="form-control">
231237
<label class="label">
232238
<span class="label-text">{{ env.label }}</span>
233-
<span class="label-text-alt">Optional</span>
239+
<span class="badge badge-sm badge-ghost">Optional</span>
234240
</label>
235241
<input
236242
v-model="configValues[env.key]"
237243
:type="env.secret ? 'password' : 'text'"
238-
:placeholder="env.key"
244+
:placeholder="configuredPlaceholder(env.key)"
239245
class="input input-bordered"
240246
/>
241247
</div>
248+
<!-- Custom env var -->
249+
<div class="divider text-xs">Add Custom Variable</div>
250+
<div class="flex gap-2">
251+
<input v-model="customEnvKey" type="text" placeholder="OPENAI_API_KEY" class="input input-bordered input-sm flex-1" />
252+
<input v-model="customEnvValue" type="password" placeholder="Value" class="input input-bordered input-sm flex-1" />
253+
<button @click="addCustomEnv" :disabled="!customEnvKey || !customEnvValue" class="btn btn-sm btn-outline">Add</button>
254+
</div>
255+
<!-- Show existing configured vars -->
256+
<div v-if="Object.keys(configValues).length > 0" class="mt-2">
257+
<div class="text-xs text-base-content/50 mb-1">Configured variables:</div>
258+
<div v-for="(val, key) in configValues" :key="key" class="flex items-center gap-2 text-sm py-1">
259+
<code class="font-mono text-xs bg-base-200 px-2 py-0.5 rounded">{{ key }}</code>
260+
<span class="text-base-content/50">{{ val.startsWith('${keyring:') ? 'stored in keyring' : 'set' }}</span>
261+
<button @click="delete configValues[key]" class="btn btn-ghost btn-xs text-error">x</button>
262+
</div>
263+
</div>
242264
</div>
243265
<div class="modal-action">
244266
<button @click="closeConfigDialog" class="btn">Cancel</button>
@@ -267,6 +289,8 @@ const scanResult = ref<any>(null)
267289
const configDialog = ref<HTMLDialogElement>()
268290
const configScanner = ref<any>(null)
269291
const configValues = ref<Record<string, string>>({})
292+
const customEnvKey = ref('')
293+
const customEnvValue = ref('')
270294
271295
const totalFindings = computed(() => overview.value?.findings_by_severity?.total || 0)
272296
@@ -334,21 +358,47 @@ async function removeScanner(id: string) {
334358
335359
function openConfigDialog(scanner: any) {
336360
configScanner.value = scanner
337-
configValues.value = {}
361+
// Pre-populate with existing configured values
362+
const existing = scanner.configured_env || {}
363+
configValues.value = { ...existing }
364+
customEnvKey.value = ''
365+
customEnvValue.value = ''
338366
configDialog.value?.showModal()
339367
}
340368
341369
function closeConfigDialog() {
342370
configDialog.value?.close()
343371
}
344372
373+
function configuredPlaceholder(key: string): string {
374+
const existing = configScanner.value?.configured_env?.[key]
375+
if (existing) {
376+
if (existing.startsWith('${keyring:')) return '(stored in keyring)'
377+
return '(configured)'
378+
}
379+
return key
380+
}
381+
382+
function addCustomEnv() {
383+
if (customEnvKey.value && customEnvValue.value) {
384+
configValues.value[customEnvKey.value] = customEnvValue.value
385+
customEnvKey.value = ''
386+
customEnvValue.value = ''
387+
}
388+
}
389+
345390
async function saveConfig() {
346391
if (!configScanner.value) return
347-
const nonEmpty: Record<string, string> = {}
392+
// Only send non-empty values that aren't keyring references (new values)
393+
const toSend: Record<string, string> = {}
348394
for (const [k, v] of Object.entries(configValues.value)) {
349-
if (v) nonEmpty[k] = v
395+
if (v && !v.startsWith('${keyring:')) {
396+
toSend[k] = v
397+
}
398+
}
399+
if (Object.keys(toSend).length > 0) {
400+
await api.configureScanner(configScanner.value.id, toSend)
350401
}
351-
await api.configureScanner(configScanner.value.id, nonEmpty)
352402
closeConfigDialog()
353403
await refresh()
354404
}

web/frontend/dist/assets/Activity-DtT8gKEq.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/frontend/dist/assets/AdminDashboard-D8pMzi14.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)