Skip to content

Conversation

@18202781743
Copy link
Contributor

@18202781743 18202781743 commented Dec 22, 2025

  1. Replaced bash -c command execution with direct process execution for
    improved security
  2. Changed from using bash shell to directly execute setxkbmap command
  3. Implemented manual parsing of setxkbmap output instead of relying on
    grep and awk through bash
  4. Added proper handling of output format including colon removal when
    present

Log: Improved security by removing bash -c command execution

Influence:

  1. Test lock screen functionality still works correctly
  2. Verify keyboard options are properly retrieved and restored
  3. Test with different keyboard configurations to ensure parsing works
    correctly
  4. Verify no regression in lock screen behavior
  5. Test security by attempting command injection (should now be
    prevented)
  6. Verify process execution works without bash shell dependencies

fix: 移除 bash -c 以提高安全性

  1. 将 bash -c 命令执行替换为直接进程执行以提高安全性
  2. 从使用 bash shell 改为直接执行 setxkbmap 命令
  3. 实现了手动解析 setxkbmap 输出,而不是通过 bash 依赖 grep 和 awk
  4. 添加了适当的输出格式处理,包括存在冒号时的移除处理

Log: 通过移除 bash -c 命令执行提高了安全性

Influence:

  1. 测试锁屏功能是否仍然正常工作
  2. 验证键盘选项是否正确获取和恢复
  3. 使用不同的键盘配置测试以确保解析正常工作
  4. 验证锁屏行为没有回归问题
  5. 测试安全性,尝试命令注入(现在应该被阻止)
  6. 验证进程执行在没有 bash shell 依赖的情况下正常工作

Summary by Sourcery

Harden keyboard option handling in the X11 lock screen flow by removing shell-based command execution and parsing setxkbmap output directly.

Bug Fixes:

  • Prevent potential command injection by replacing bash -c usage with direct setxkbmap process execution.

Enhancements:

  • Parse setxkbmap -query output manually in C++ to derive the current keyboard options, including handling of optional trailing colons in the option field.

1. Replaced bash -c command execution with direct process execution for
improved security
2. Changed from using bash shell to directly execute setxkbmap command
3. Implemented manual parsing of setxkbmap output instead of relying on
grep and awk through bash
4. Added proper handling of output format including colon removal when
present

Log: Improved security by removing bash -c command execution

Influence:
1. Test lock screen functionality still works correctly
2. Verify keyboard options are properly retrieved and restored
3. Test with different keyboard configurations to ensure parsing works
correctly
4. Verify no regression in lock screen behavior
5. Test security by attempting command injection (should now be
prevented)
6. Verify process execution works without bash shell dependencies

fix: 移除 bash -c 以提高安全性

1. 将 bash -c 命令执行替换为直接进程执行以提高安全性
2. 从使用 bash shell 改为直接执行 setxkbmap 命令
3. 实现了手动解析 setxkbmap 输出,而不是通过 bash 依赖 grep 和 awk
4. 添加了适当的输出格式处理,包括存在冒号时的移除处理

Log: 通过移除 bash -c 命令执行提高了安全性

Influence:
1. 测试锁屏功能是否仍然正常工作
2. 验证键盘选项是否正确获取和恢复
3. 使用不同的键盘配置测试以确保解析正常工作
4. 验证锁屏行为没有回归问题
5. 测试安全性,尝试命令注入(现在应该被阻止)
6. 验证进程执行在没有 bash shell 依赖的情况下正常工作
@deepin-ci-robot
Copy link

deepin pr auto review

我来对这段代码的修改进行审查:

  1. 语法逻辑:
  • 修改后的代码逻辑正确,将原来的bash命令替换为更安全的Qt API调用
  • 通过split和字符串处理来模拟原来的grep和awk命令的功能
  • 代码结构清晰,注释充分
  1. 代码质量:
  • 优点:
    • 避免了使用bash命令执行,降低了系统依赖性
    • 使用Qt的字符串处理API,提高了代码的可维护性
    • 添加了适当的错误处理(检查parts.size())
  • 改进建议:
    • 可以考虑将字符串处理逻辑封装成单独的函数,提高代码复用性
    • 可以添加更多的错误处理,比如检查process的执行状态
  1. 代码性能:
  • 新代码的性能可能会略低于原来的bash命令,因为:
    • 需要进行多次字符串分割和处理
    • Qt的字符串操作可能比bash的文本处理工具慢
  • 但考虑到这段代码执行频率不高,性能影响可以忽略
  1. 代码安全:
  • 显著改进:
    • 避免了shell注入的风险
    • 不再依赖外部命令(grep、awk)
    • 使用Qt的标准API,更加安全可靠

建议进一步改进:

  1. 添加process执行状态检查:
if (process.exitCode() != 0) {
    qWarning() << "Failed to execute setxkbmap -query";
    return;
}
  1. 可以考虑使用QRegularExpression来处理输出,代码会更简洁:
QRegularExpression re(R"(option:\s*(\S+))");
QRegularExpressionMatch match = re.match(output);
if (match.hasMatch()) {
    originMap = match.captured(1);
}
  1. 添加超时处理:
if (!process.waitForFinished(5000)) { // 5秒超时
    qWarning() << "setxkbmap command timeout";
    process.kill();
    return;
}

总体来说,这是一个很好的改进,提高了代码的安全性和可维护性,虽然可能会有轻微的性能影响,但在实际使用中不会造成问题。

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 22, 2025

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Replaces a shell-based setxkbmap invocation with direct process execution and in-code parsing of its output to improve security and robustness around keyboard option handling in the X11 lock screen path.

Sequence diagram for direct setxkbmap execution and parsing in x11LockScreen

sequenceDiagram
    participant ShutdownApplet
    participant QProcess
    participant setxkbmap

    ShutdownApplet->>QProcess: start(/usr/bin/setxkbmap, -query)
    QProcess->>setxkbmap: exec -query
    setxkbmap-->>QProcess: stdout (keyboard layout info)
    QProcess-->>ShutdownApplet: readAllStandardOutput()
    ShutdownApplet->>ShutdownApplet: split output into lines
    loop each_line
        ShutdownApplet->>ShutdownApplet: check line contains option
        alt line contains option
            ShutdownApplet->>ShutdownApplet: split by space (SkipEmptyParts)
            alt parts size >= 2
                ShutdownApplet->>ShutdownApplet: originMap = parts[1]
                alt originMap endsWith colon
                    ShutdownApplet->>ShutdownApplet: originMap = originMap without trailing colon
                end
                ShutdownApplet->>ShutdownApplet: break loop
            end
        end
    end
    ShutdownApplet->>QProcess: start(/usr/bin/setxkbmap, -option grab:break_actions)
Loading

Flow diagram for parsing setxkbmap output to extract keyboard options

flowchart TD
    A[start x11LockScreen keyboard option retrieval] --> B[run /usr/bin/setxkbmap -query via QProcess]
    B --> C[read stdout as UTF-8 string]
    C --> D[split output by newline into lines]
    D --> E{for each line}
    E -->|next line| F{line contains option}
    F -->|no| E
    F -->|yes| G[split line by spaces with SkipEmptyParts into parts]
    G --> H{parts size >= 2}
    H -->|no| E
    H -->|yes| I[set originMap to parts index 1]
    I --> J{originMap ends with colon}
    J -->|yes| K[originMap = originMap without trailing colon]
    J -->|no| L[keep originMap unchanged]
    K --> M[break loop]
    L --> M[break loop]
    M --> N[use originMap when setting lock screen keyboard options]
Loading

File-Level Changes

Change Details Files
Replace bash -c pipeline with direct setxkbmap execution and C++ parsing of its output to remove shell injection risk.
  • Start the QProcess directly with /usr/bin/setxkbmap -query instead of invoking bash -c with a piped command string.
  • Capture the full setxkbmap -query output and split it into individual lines for inspection.
  • Iterate over lines to find the one containing the keyboard options field that was previously extracted via grep.
  • Split the matching line on spaces, skipping empty fields, to emulate awk '{print $2}'.
  • Assign the second token as the originMap value and strip a trailing colon if present to normalize the option string before reuse.
applets/dde-shutdown/shutdownapplet.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The parsing logic for the setxkbmap -query output will be more robust if you target the expected key explicitly (e.g., check line.startsWith("options:") instead of line.contains("option")) to avoid accidental matches on unrelated lines.
  • Consider using process.waitForFinished()'s return value and/or checking process.exitStatus()/exitCode() to handle failures from setxkbmap explicitly, so that an unexpected error doesn’t silently leave originMap unset.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The parsing logic for the `setxkbmap -query` output will be more robust if you target the expected key explicitly (e.g., check `line.startsWith("options:")` instead of `line.contains("option")`) to avoid accidental matches on unrelated lines.
- Consider using `process.waitForFinished()`'s return value and/or checking `process.exitStatus()/exitCode()` to handle failures from `setxkbmap` explicitly, so that an unexpected error doesn’t silently leave `originMap` unset.

## Individual Comments

### Comment 1
<location> `applets/dde-shutdown/shutdownapplet.cpp:89-91` </location>
<code_context>
-    originMap = QString::fromUtf8(process.readAllStandardOutput()).trimmed();
+    QString output = QString::fromUtf8(process.readAllStandardOutput());
+    QStringList lines = output.split('\n');
+    for (const QString &line : lines) {
+        // 查找包含 "option" 的行(原 grep option 命令)
+        if (line.contains("option")) {
+            // 模拟 awk -F ' ' '{print $2}'
+            // 按空格分割,跳过空字符串
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Tighten the line match to avoid accidental matches on unrelated text.

`line.contains("option")` matches the old `grep option` behavior but is overly broad and could pick up unrelated text (e.g. comments or localized strings). Since `setxkbmap -query` has a structured output, prefer a stricter condition like `line.startsWith("options:")` (or similar) to ensure you only parse the intended line.

```suggestion
    for (const QString &line : lines) {
        // 只匹配以 "options:" 开头的行,避免误匹配其他包含 "option" 的文本
        if (line.startsWith("options:")) {
```
</issue_to_address>

### Comment 2
<location> `applets/dde-shutdown/shutdownapplet.cpp:88-104` </location>
<code_context>
+        if (line.contains("option")) {
+            // 模拟 awk -F ' ' '{print $2}'
+            // 按空格分割,跳过空字符串
+            QStringList parts = line.split(' ', Qt::SkipEmptyParts);
+            if (parts.size() >= 2) {
+                originMap = parts[1];
+                // 如果第二个字段以冒号结尾,可能需要进一步处理
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider guarding against malformed output or unexpected field positions.

This parsing relies on the options value always being the second non-empty token, as in the old `awk '{print $2}'` usage. If `setxkbmap -query` output changes (extra columns, different spacing, tabs), it may silently set a wrong `originMap`. To make this more robust, explicitly skip the label token and join the rest, or use a regex to capture everything after `options:` so spacing and extra fields don’t affect the result.

```suggestion
    QStringList lines = output.split('\n');
    for (const QString &line : lines) {
        // 查找以 "options:" 开头的行,避免依赖字段位置
        const QString trimmed = line.trimmed();
        const QString label = QStringLiteral("options:");
        if (trimmed.startsWith(label)) {
            // 提取 "options:" 之后的所有内容并去除首尾空白
            const QString value = trimmed.mid(label.length()).trimmed();
            if (!value.isEmpty()) {
                originMap = value;
            }
            break;
        }
    }
```
</issue_to_address>

### Comment 3
<location> `applets/dde-shutdown/shutdownapplet.cpp:91-103` </location>
<code_context>
+            if (parts.size() >= 2) {
+                originMap = parts[1];
+                // 如果第二个字段以冒号结尾,可能需要进一步处理
+                if (originMap.endsWith(':')) {
+                    originMap = originMap.left(originMap.length() - 1);
+                }
+            }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The trailing-colon handling may be unnecessary and could hide real values.

In typical `setxkbmap -query` output, the colon belongs to the first token (e.g. `options:`), not the value, so `parts[1]` shouldn’t end with `:`. Unconditionally stripping a trailing colon from `originMap` could silently change valid values if the output format changes. Unless you’ve seen actual cases where the value ends with `:`, consider removing this special handling.

```suggestion
        if (line.contains("option")) {
            // 模拟 awk -F ' ' '{print $2}'
            // 按空格分割,跳过空字符串
            QStringList parts = line.split(' ', Qt::SkipEmptyParts);
            if (parts.size() >= 2) {
                // 直接使用第二个字段作为原始配置值,不进行额外的冒号处理
                originMap = parts[1];
            }
            break;
        }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +89 to +91
for (const QString &line : lines) {
// 查找包含 "option" 的行(原 grep option 命令)
if (line.contains("option")) {
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): Tighten the line match to avoid accidental matches on unrelated text.

line.contains("option") matches the old grep option behavior but is overly broad and could pick up unrelated text (e.g. comments or localized strings). Since setxkbmap -query has a structured output, prefer a stricter condition like line.startsWith("options:") (or similar) to ensure you only parse the intended line.

Suggested change
for (const QString &line : lines) {
// 查找包含 "option" 的行(原 grep option 命令)
if (line.contains("option")) {
for (const QString &line : lines) {
// 只匹配以 "options:" 开头的行,避免误匹配其他包含 "option" 的文本
if (line.startsWith("options:")) {

Comment on lines +88 to +104
QStringList lines = output.split('\n');
for (const QString &line : lines) {
// 查找包含 "option" 的行(原 grep option 命令)
if (line.contains("option")) {
// 模拟 awk -F ' ' '{print $2}'
// 按空格分割,跳过空字符串
QStringList parts = line.split(' ', Qt::SkipEmptyParts);
if (parts.size() >= 2) {
originMap = parts[1];
// 如果第二个字段以冒号结尾,可能需要进一步处理
if (originMap.endsWith(':')) {
originMap = originMap.left(originMap.length() - 1);
}
}
break;
}
}
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): Consider guarding against malformed output or unexpected field positions.

This parsing relies on the options value always being the second non-empty token, as in the old awk '{print $2}' usage. If setxkbmap -query output changes (extra columns, different spacing, tabs), it may silently set a wrong originMap. To make this more robust, explicitly skip the label token and join the rest, or use a regex to capture everything after options: so spacing and extra fields don’t affect the result.

Suggested change
QStringList lines = output.split('\n');
for (const QString &line : lines) {
// 查找包含 "option" 的行(原 grep option 命令)
if (line.contains("option")) {
// 模拟 awk -F ' ' '{print $2}'
// 按空格分割,跳过空字符串
QStringList parts = line.split(' ', Qt::SkipEmptyParts);
if (parts.size() >= 2) {
originMap = parts[1];
// 如果第二个字段以冒号结尾,可能需要进一步处理
if (originMap.endsWith(':')) {
originMap = originMap.left(originMap.length() - 1);
}
}
break;
}
}
QStringList lines = output.split('\n');
for (const QString &line : lines) {
// 查找以 "options:" 开头的行,避免依赖字段位置
const QString trimmed = line.trimmed();
const QString label = QStringLiteral("options:");
if (trimmed.startsWith(label)) {
// 提取 "options:" 之后的所有内容并去除首尾空白
const QString value = trimmed.mid(label.length()).trimmed();
if (!value.isEmpty()) {
originMap = value;
}
break;
}
}

Comment on lines +91 to +103
if (line.contains("option")) {
// 模拟 awk -F ' ' '{print $2}'
// 按空格分割,跳过空字符串
QStringList parts = line.split(' ', Qt::SkipEmptyParts);
if (parts.size() >= 2) {
originMap = parts[1];
// 如果第二个字段以冒号结尾,可能需要进一步处理
if (originMap.endsWith(':')) {
originMap = originMap.left(originMap.length() - 1);
}
}
break;
}
Copy link

Choose a reason for hiding this comment

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

suggestion (bug_risk): The trailing-colon handling may be unnecessary and could hide real values.

In typical setxkbmap -query output, the colon belongs to the first token (e.g. options:), not the value, so parts[1] shouldn’t end with :. Unconditionally stripping a trailing colon from originMap could silently change valid values if the output format changes. Unless you’ve seen actual cases where the value ends with :, consider removing this special handling.

Suggested change
if (line.contains("option")) {
// 模拟 awk -F ' ' '{print $2}'
// 按空格分割,跳过空字符串
QStringList parts = line.split(' ', Qt::SkipEmptyParts);
if (parts.size() >= 2) {
originMap = parts[1];
// 如果第二个字段以冒号结尾,可能需要进一步处理
if (originMap.endsWith(':')) {
originMap = originMap.left(originMap.length() - 1);
}
}
break;
}
if (line.contains("option")) {
// 模拟 awk -F ' ' '{print $2}'
// 按空格分割,跳过空字符串
QStringList parts = line.split(' ', Qt::SkipEmptyParts);
if (parts.size() >= 2) {
// 直接使用第二个字段作为原始配置值,不进行额外的冒号处理
originMap = parts[1];
}
break;
}

@deepin-ci-robot
Copy link

[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

@18202781743 18202781743 merged commit cf2fe93 into linuxdeepin:master Dec 22, 2025
9 of 11 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