Skip to content

feat(shortcut): add custom shortcut CRUD persistence#98

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
yixinshark:feat/shortcut-custom-crud-persistence
Jul 8, 2026
Merged

feat(shortcut): add custom shortcut CRUD persistence#98
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
yixinshark:feat/shortcut-custom-crud-persistence

Conversation

@yixinshark

Copy link
Copy Markdown
Contributor

Summary

  • add dynamic shortcut category metadata for Wayland shortcut grouping
  • add custom shortcut CRUD persistence and display-only support
  • update shortcut category i18n extraction and translation entries

Test

  • Built shortcut-related targets before submitting this branch to the stack base PR.
  • Not rerun in this turn; current request only creates the upstream master PR.

Related: #96

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @yixinshark, your pull request is larger than the review limit of 150000 diff characters

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:40分

■ 【总体评价】

代码实现了快捷键类别字符串化及自定义快捷键完整生命周期管理,但存在高危权限提升漏洞
逻辑正确但因DBus接口允许注入任意命令且守护进程高权限运行扣60分

■ 【详细分析】

  • 1.语法逻辑(基本正确)✓

代码结构清晰,事务处理模型的回滚逻辑严密。在 ConfigLoader::removeCustomShortcut 中,先从 m_configs 取出配置,根据是否有效进行断开连接或释放,最后统一清理内存和列表,逻辑连贯。CustomShortcutStore::normalizeSubPath 有效拦截了路径遍历字符。CustomShortcutTransactionapplyRuntimepersistAdd 等方法在失败时均能逐级回滚运行时状态和持久化数据。
潜在问题:在新增的 org.deepin.shortcut.json 中,modifiable 字段的 description 被写为"不能修改快捷键",与其 value: truename[zh_CN] 的语义矛盾,属于配置描述错误。
建议:修正 JSON 配置中 modifiabledescription 为"能修改快捷键"以保持语义一致。

  • 2.代码质量(良好)✓

代码遵循了 Qt 和 C++ 的命名规范,类职责划分合理(Store 负责持久化,Transaction 负责原子操作)。注释恰到好处地解释了事务模型的设计意图。防御性编程做得较好,如 createCustomShortcutId 中使用 UUID 并检查冲突,runtimeCustomShortcutCount 限制了最大数量。
潜在问题:CustomShortcutStore::save 方法中通过 qDebug 打印了 command length,虽然未泄露命令本体,但在生产环境的日志中保留此类调试信息不够精简。
建议:将 CustomShortcutStore::save 中的详细参数日志级别降级或移除,仅保留必要的错误警告。

  • 3.代码性能(无性能问题)✓

新增逻辑的时间复杂度均在可接受范围内。runtimeCustomShortcutCount 虽然遍历了整个 Map,但受限于 MaxCustomShortcutCount(200)的上限,开销极小。ListCategories 中的排序操作作用于少量去重后的类别集合,不构成瓶颈。normalizeSubPath 的字符串检查操作均为线性且针对短字符串。
建议:无需优化,当前实现已足够高效。

  • 4.代码安全(存在1个安全漏洞)✕

漏洞对比统计:新增漏洞 1 个,减少漏洞 0 个,持平 0 个
总体风险描述:新增的 DBus 接口允许传入未经验证的 shell 命令,若 dde-daemon 以高权限运行且缺乏严格的 Polkit 拦截,攻击面将直达系统底层。

  • 安全漏洞1([无] ):[本地权限提升/命令注入] 在 [KeybindingManager::AddCustomShortcut / keybindingmanager.cpp] 中,[输入源为 DBus 调用参数 action,触发方式为恶意用户调用该接口设置恶意命令(如反弹 shell)并绑定快捷键,当快捷键被触发时,若 ActionExecutor 以 root 权限执行 triggerValue 中的命令,将导致系统被完全控制。isValidCustomShortcutCommand 仅校验了长度和是否包含控制字符,未做任何白名单或沙箱限制] ——非常重要

  • 建议:在 isValidCustomShortcutCommand 中增加可执行文件路径的白名单校验(如仅允许 /usr/bin/usr/local/bin 下的程序),或者在 AddCustomShortcut 接口入口处强制调用 calledFromDBus() 并结合 Polkit 验证调用者是否具有 org.deepin.dde.keybinding.modify 等高权限动作,拒绝无权限的命令写入。

■ 【改进建议代码示例】

// 在 keybindingmanager.cpp 中增加白名单校验逻辑
#include <QFileInfo>

static const QStringList &allowedCommandPrefixes()
{
    static const QStringList prefixes = {
        QStringLiteral("/usr/bin/"),
        QStringLiteral("/usr/local/bin/"),
        QStringLiteral("/usr/sbin/")
    };
    return prefixes;
}

static bool isValidCustomShortcutCommand(const QString &command)
{
    if (command.isEmpty()
        || command.size() > MaxCustomShortcutCommandLength
        || containsControlCharacter(command)) {
        return false;
    }

    // 防止通过 shell 元字符进行命令注入
    if (command.contains(QLatin1Char('|')) || command.contains(QLatin1Char('&')) 
        || command.contains(QLatin1Char(';')) || command.contains(QLatin1Char('>'))
        || command.contains(QLatin1Char('<')) || command.contains(QLatin1Char('$'))
        || command.contains(QLatin1Char('`')) || command.contains(QLatin1Char('('))) {
        qWarning() << "Invalid command: contains shell metacharacters";
        return false;
    }

    // 提取第一段作为可执行文件路径进行白名单校验
    const QString executablePath = command.split(QLatin1Char(' ')).first();
    const QFileInfo fileInfo(executablePath);
    if (!fileInfo.isAbsolute()) {
        qWarning() << "Invalid command: must use absolute path";
        return false;
    }

    bool allowed = false;
    for (const QString &prefix : allowedCommandPrefixes()) {
        if (executablePath.startsWith(prefix)) {
            allowed = true;
            break;
        }
    }
    
    if (!allowed) {
        qWarning() << "Invalid command: executable not in allowed paths" << executablePath;
    }
    return allowed;
}

Add runtime custom shortcut CRUD (AddCustomShortcut/ModifyCustomShortcut/DeleteCustomShortcut) to KeybindingManager with input validation, conflict handling and commit-failure rollback. Add GetShortcutCommand so the control center can fetch a custom shortcut command separately.

Persist runtime custom shortcuts through user-level DConfig plus a custom shortcut subpath registry. Keep Add on full save/create semantics, use updateCustomShortcut for Modify so existing entries update only changed fields, and roll back persisted state when registration or conflict persistence fails.

Introduce display-only shortcut configs so modifiable shortcuts with no hotkey remain visible as "None" after their binding is taken. Keep these entries in the runtime map and expose them through ListAllShortcuts.

Remove the unused checkConflictForConfig helper after conflict checks stayed on the actual LookupConflictShortcut/registerShortcut paths.

新增自定义快捷键运行时增删改(AddCustomShortcut/ModifyCustomShortcut/DeleteCustomShortcut),包含输入校验、冲突处理和提交失败回滚;新增 GetShortcutCommand,供控制中心单独获取自定义快捷键命令。

通过用户级 DConfig 和自定义快捷键 subpath 注册表持久化运行时自定义快捷键。Add 继续使用完整创建/保存语义,Modify 改用 updateCustomShortcut,仅更新已有条目的变化字段,并在注册或冲突持久化失败时回滚持久化状态。

引入 display-only 快捷键配置,使可修改但无热键的快捷键在绑定被其他快捷键占用后仍能以“无”显示。此类条目保留在运行时映射中,并通过 ListAllShortcuts 暴露。

删除未使用的 checkConflictForConfig 辅助函数,因为真实冲突检测仍走 LookupConflictShortcut/registerShortcut 路径。

Log: feat(shortcut): add custom shortcut CRUD and display-only support
Change-Id: Ie59dd3cb4e2575eea15974b9454eefc0ff484bfa
@yixinshark yixinshark force-pushed the feat/shortcut-custom-crud-persistence branch from d713203 to 2de4961 Compare July 8, 2026 09:20
@yixinshark

Copy link
Copy Markdown
Contributor Author

deepin pr auto review

★ 总体评分:40分

■ 【总体评价】

代码实现了快捷键类别字符串化及自定义快捷键完整生命周期管理,但存在高危权限提升漏洞
逻辑正确但因DBus接口允许注入任意命令且守护进程高权限运行扣60分

■ 【详细分析】

  • 1.语法逻辑(基本正确)✓

代码结构清晰,事务处理模型的回滚逻辑严密。在 ConfigLoader::removeCustomShortcut 中,先从 m_configs 取出配置,根据是否有效进行断开连接或释放,最后统一清理内存和列表,逻辑连贯。CustomShortcutStore::normalizeSubPath 有效拦截了路径遍历字符。CustomShortcutTransactionapplyRuntimepersistAdd 等方法在失败时均能逐级回滚运行时状态和持久化数据。
潜在问题:在新增的 org.deepin.shortcut.json 中,modifiable 字段的 description 被写为"不能修改快捷键",与其 value: truename[zh_CN] 的语义矛盾,属于配置描述错误。
建议:修正 JSON 配置中 modifiabledescription 为"能修改快捷键"以保持语义一致。

  • 2.代码质量(良好)✓

代码遵循了 Qt 和 C++ 的命名规范,类职责划分合理(Store 负责持久化,Transaction 负责原子操作)。注释恰到好处地解释了事务模型的设计意图。防御性编程做得较好,如 createCustomShortcutId 中使用 UUID 并检查冲突,runtimeCustomShortcutCount 限制了最大数量。
潜在问题:CustomShortcutStore::save 方法中通过 qDebug 打印了 command length,虽然未泄露命令本体,但在生产环境的日志中保留此类调试信息不够精简。
建议:将 CustomShortcutStore::save 中的详细参数日志级别降级或移除,仅保留必要的错误警告。

  • 3.代码性能(无性能问题)✓

新增逻辑的时间复杂度均在可接受范围内。runtimeCustomShortcutCount 虽然遍历了整个 Map,但受限于 MaxCustomShortcutCount(200)的上限,开销极小。ListCategories 中的排序操作作用于少量去重后的类别集合,不构成瓶颈。normalizeSubPath 的字符串检查操作均为线性且针对短字符串。
建议:无需优化,当前实现已足够高效。

  • 4.代码安全(存在1个安全漏洞)✕

漏洞对比统计:新增漏洞 1 个,减少漏洞 0 个,持平 0 个
总体风险描述:新增的 DBus 接口允许传入未经验证的 shell 命令,若 dde-daemon 以高权限运行且缺乏严格的 Polkit 拦截,攻击面将直达系统底层。

  • 安全漏洞1([无] ):[本地权限提升/命令注入] 在 [KeybindingManager::AddCustomShortcut / keybindingmanager.cpp] 中,[输入源为 DBus 调用参数 action,触发方式为恶意用户调用该接口设置恶意命令(如反弹 shell)并绑定快捷键,当快捷键被触发时,若 ActionExecutor 以 root 权限执行 triggerValue 中的命令,将导致系统被完全控制。isValidCustomShortcutCommand 仅校验了长度和是否包含控制字符,未做任何白名单或沙箱限制] ——非常重要
  • 建议:在 isValidCustomShortcutCommand 中增加可执行文件路径的白名单校验(如仅允许 /usr/bin/usr/local/bin 下的程序),或者在 AddCustomShortcut 接口入口处强制调用 calledFromDBus() 并结合 Polkit 验证调用者是否具有 org.deepin.dde.keybinding.modify 等高权限动作,拒绝无权限的命令写入。

■ 【改进建议代码示例】

// 在 keybindingmanager.cpp 中增加白名单校验逻辑
#include <QFileInfo>

static const QStringList &allowedCommandPrefixes()
{
    static const QStringList prefixes = {
        QStringLiteral("/usr/bin/"),
        QStringLiteral("/usr/local/bin/"),
        QStringLiteral("/usr/sbin/")
    };
    return prefixes;
}

static bool isValidCustomShortcutCommand(const QString &command)
{
    if (command.isEmpty()
        || command.size() > MaxCustomShortcutCommandLength
        || containsControlCharacter(command)) {
        return false;
    }

    // 防止通过 shell 元字符进行命令注入
    if (command.contains(QLatin1Char('|')) || command.contains(QLatin1Char('&')) 
        || command.contains(QLatin1Char(';')) || command.contains(QLatin1Char('>'))
        || command.contains(QLatin1Char('<')) || command.contains(QLatin1Char('$'))
        || command.contains(QLatin1Char('`')) || command.contains(QLatin1Char('('))) {
        qWarning() << "Invalid command: contains shell metacharacters";
        return false;
    }

    // 提取第一段作为可执行文件路径进行白名单校验
    const QString executablePath = command.split(QLatin1Char(' ')).first();
    const QFileInfo fileInfo(executablePath);
    if (!fileInfo.isAbsolute()) {
        qWarning() << "Invalid command: must use absolute path";
        return false;
    }

    bool allowed = false;
    for (const QString &prefix : allowedCommandPrefixes()) {
        if (executablePath.startsWith(prefix)) {
            allowed = true;
            break;
        }
    }
    
    if (!allowed) {
        qWarning() << "Invalid command: executable not in allowed paths" << executablePath;
    }
    return allowed;
}

#96
参看这个pr,为误报风险,96pr为Ai提错了分支导致已经合并,然后又重新提98pr.

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, yixinshark

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@yixinshark

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

This pr force merged! (status: blocked)

@deepin-bot deepin-bot Bot merged commit 50340c6 into linuxdeepin:master Jul 8, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants