新增: 信任所有目录扩展至敏感目录放行

开关语义升级为完全放行,含 .ssh/.aws 等黑名单目录(产品拍板体验优先,用户知情选择)
validate_path 与 is_authorized 双拦截点经进程级标志同源放行,audit 层每轮工具执行前刷新
设置页开启时二次确认,正文明示敏感凭据可进对话上下文风险自担;对话区常驻放行横幅既有
测试以串行闸防并行互踩进程级标志
This commit is contained in:
lxy
2026-10-01 02:40:49 +08:00
parent 63779e8d69
commit 3e239a108f
8 changed files with 204 additions and 25 deletions
+65 -3
View File
@@ -8,11 +8,14 @@
<!-- 说明 -->
<p class="allowed-dirs-desc">{{ $t('settings.descAllowedDirs') }}</p>
<!-- 「信任所有目录」总开关:开启后白名单外路径不再逐次弹授权(后端黑名单仍硬拒)。
走 appSettings KV(df-ai-path-auth-bypass),后端 process_tool_calls 每轮读取,改后即时生效。 -->
<!-- 「信任所有目录」总开关:开启后白名单外路径不再逐次弹授权(后端黑名单拦截同步放行)。
走 appSettings KV(df-ai-path-auth-bypass),后端 process_tool_calls 每轮读取,改后即时生效。
开启方向二次确认(全放敏感目录须确认+风险自担):checked 单向绑定 + @change 裁决,
确认前不写 ref。若 v-model 直写,弹窗期间 useSetting 的 debounced 落库已把 true 推给后端,
取消也收不回这段已放行窗口。 -->
<SettingRow :label="$t('settings.labelPathAuthBypass')" :desc="$t('settings.descPathAuthBypass')">
<label class="toggle">
<input type="checkbox" v-model="pathAuthBypass" />
<input type="checkbox" :checked="pathAuthBypass" @change="onPathAuthBypassChange" />
<span class="toggle-slider"></span>
</label>
</SettingRow>
@@ -82,6 +85,20 @@
<p class="script-safety-note">{{ $t('settings.noteScriptSafety') }}</p>
</div>
</section>
<!-- 「信任所有目录」二次确认弹层(同 AdvancedSection 的 all 接管确认:自渲染独立弹层,
msg 拼标题+正文,confirm-msg 用 pre-line 保留换行) -->
<Transition name="confirm">
<div v-if="confirmState.visible" class="confirm-mask" @click.self="answerConfirm(false)">
<div class="confirm-box">
<div class="confirm-msg">{{ confirmState.msg }}</div>
<div class="confirm-actions">
<button class="btn btn-ghost btn-sm" @click="answerConfirm(false)">{{ $t('common.cancel') }}</button>
<button class="btn btn-danger btn-sm" @click="answerConfirm(true)">{{ confirmState.dangerLabel || $t('common.confirm') }}</button>
</div>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
@@ -91,6 +108,7 @@ import { useI18n } from 'vue-i18n'
import { invoke } from '@tauri-apps/api/core'
import { aiApi, settingsApi } from '@/api'
import { useAppSettingsStore } from '@/stores/appSettings'
import { useConfirm } from '@/composables/useConfirm'
import SettingRow from './SettingRow.vue'
const { t } = useI18n()
@@ -111,6 +129,24 @@ const saving = ref(false)
// 「信任所有目录」总开关(useSetting 双向绑 KV,写走 debounced 落库,后端每轮工具执行前读)
const pathAuthBypass = appSettings.useSetting<boolean>('df-ai-path-auth-bypass', false)
// useConfirm 真实 API:返回 { confirmState, confirmDialog(msg, dangerLabel), answerConfirm(ok) },
// confirmDialog 返回 Promise<boolean>(true=确认),弹层 UI 在本组件模板尾部自渲染。
const { confirmState, confirmDialog, answerConfirm } = useConfirm()
// 开启方向(false→true)二次确认:确认才落 true,取消仅回弹。关闭方向不拦截直接落。
async function onPathAuthBypassChange(e: Event) {
const input = e.target as HTMLInputElement
if (!input.checked) {
pathAuthBypass.value = false
return
}
input.checked = false
const msg = t('settings.pathAuthBypassConfirmTitle') + '\n\n' + t('settings.pathAuthBypassConfirmBody')
if (await confirmDialog(msg, t('common.confirm'))) {
pathAuthBypass.value = true
}
}
async function load() {
loading.value = true
try {
@@ -262,4 +298,30 @@ onMounted(load)
.script-safety-note {
font-size: 11px; color: var(--df-text-dim); margin: 4px 0 0; line-height: 1.5;
}
/* 「信任所有目录」二次确认弹层(同 AdvancedSection) */
.confirm-mask {
position: fixed;
inset: 0;
z-index: 1100;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
}
.confirm-box {
background: var(--df-bg-card);
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius-lg);
padding: 20px;
min-width: 280px;
max-width: 420px;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
}
.confirm-msg { font-size: 13px; color: var(--df-text); line-height: 1.5; margin-bottom: 16px; white-space: pre-line; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 8px; }
.btn-danger { background: var(--df-danger); color: #fff; }
.btn-danger:hover { filter: brightness(1.1); }
.confirm-enter-active, .confirm-leave-active { transition: opacity 0.15s; }
.confirm-enter-from, .confirm-leave-to { opacity: 0; }
</style>